Decompiled source of ServeYouRight v1.0.4

ServeYouRight.dll

Decompiled a month ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("ServeYouRight")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("sighsorry")]
[assembly: AssemblyProduct("ServeYouRight")]
[assembly: AssemblyCopyright("Copyright ©  2021")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("4358610B-F3F4-4843-B7AF-98B7BC60DCDE")]
[assembly: AssemblyFileVersion("1.0.4")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.4.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 ServerSyncModTemplate
{
	[BepInPlugin("sighsorry.ServeYouRight", "ServeYouRight", "1.0.4")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public class ServerSyncModTemplatePlugin : BaseUnityPlugin
	{
		public enum Toggle
		{
			On = 1,
			Off = 0
		}

		private class ConfigurationManagerAttributes
		{
			public int? Order;
		}

		internal const string ModName = "ServeYouRight";

		internal const string ModVersion = "1.0.4";

		internal const string Author = "sighsorry";

		private const string ModGUID = "sighsorry.ServeYouRight";

		private static string ConfigFileName = "sighsorry.ServeYouRight.cfg";

		private static string ConfigFileFullPath;

		private static ServerSyncModTemplatePlugin? _instance;

		private readonly Harmony _harmony = new Harmony("sighsorry.ServeYouRight");

		public static readonly ManualLogSource ServerSyncModTemplateLogger;

		private FileSystemWatcher? _watcher;

		private readonly object _reloadLock = new object();

		private static readonly object DynamicConfigLock;

		private DateTime _lastConfigReloadTime;

		private static readonly TimeSpan ConfigReloadDelay;

		private static readonly Dictionary<string, PerModCategoryConfig> PerModConfigs;

		private static bool _pendingDynamicConfigSave;

		public void Awake()
		{
			_instance = this;
			RunWithConfigAutoSaveDisabled(delegate
			{
				JotunnBridge.InitializeAndEnableModQuery();
				Localization.OnLanguageChange = (Action)Delegate.Combine(Localization.OnLanguageChange, new Action(OnLanguageChange));
				Assembly executingAssembly = Assembly.GetExecutingAssembly();
				_harmony.PatchAll(executingAssembly);
				SaveConfigWithoutWatcher();
				SetupWatcher();
			});
		}

		private void OnDestroy()
		{
			Localization.OnLanguageChange = (Action)Delegate.Remove(Localization.OnLanguageChange, new Action(OnLanguageChange));
			_watcher?.Dispose();
			_watcher = null;
			RunWithConfigAutoSaveDisabled(SaveConfigWithoutWatcher);
		}

		private void OnLanguageChange()
		{
			FeasterFoodInjector.RefreshFoodPiecesAndMenu(ObjectDB.instance);
		}

		private void SetupWatcher()
		{
			_watcher = new FileSystemWatcher(Paths.ConfigPath, ConfigFileName);
			_watcher.Changed += ReadConfigValues;
			_watcher.Created += ReadConfigValues;
			_watcher.Renamed += ReadConfigValues;
			_watcher.IncludeSubdirectories = true;
			_watcher.SynchronizingObject = ThreadingHelper.SynchronizingObject;
			_watcher.EnableRaisingEvents = true;
		}

		private void ReadConfigValues(object sender, FileSystemEventArgs e)
		{
			DateTime utcNow = DateTime.UtcNow;
			if (utcNow - _lastConfigReloadTime < ConfigReloadDelay)
			{
				return;
			}
			_lastConfigReloadTime = utcNow;
			lock (_reloadLock)
			{
				if (!File.Exists(ConfigFileFullPath))
				{
					ServerSyncModTemplateLogger.LogWarning((object)"Config file does not exist. Skipping reload.");
					return;
				}
				try
				{
					ServerSyncModTemplateLogger.LogDebug((object)"Reloading configuration...");
					ReloadConfigValues();
					ServerSyncModTemplateLogger.LogInfo((object)"Configuration reload complete.");
				}
				catch (Exception ex)
				{
					ServerSyncModTemplateLogger.LogError((object)("Error reloading configuration: " + ex.Message));
				}
				finally
				{
					_lastConfigReloadTime = DateTime.UtcNow;
				}
			}
		}

		private void ReloadConfigValues()
		{
			RunWithConfigAutoSaveDisabled(delegate
			{
				((BaseUnityPlugin)this).Config.Reload();
				FeasterFoodInjector.RefreshFoodPiecesAndMenu(ObjectDB.instance);
			});
		}

		private void RunWithConfigAutoSaveDisabled(Action action)
		{
			bool saveOnConfigSet = ((BaseUnityPlugin)this).Config.SaveOnConfigSet;
			((BaseUnityPlugin)this).Config.SaveOnConfigSet = false;
			try
			{
				action();
			}
			finally
			{
				((BaseUnityPlugin)this).Config.SaveOnConfigSet = saveOnConfigSet;
			}
		}

		private void SaveConfigWithoutWatcher()
		{
			FileSystemWatcher watcher = _watcher;
			bool flag = watcher?.EnableRaisingEvents ?? false;
			if (flag)
			{
				watcher.EnableRaisingEvents = false;
			}
			try
			{
				((BaseUnityPlugin)this).Config.Save();
			}
			finally
			{
				if (flag && watcher != null)
				{
					watcher.EnableRaisingEvents = true;
				}
			}
		}

		internal static bool UseModSpecificTab(FoodSourceMod mod, PieceCategory category)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Expected I4, but got Unknown
			PerModCategoryConfig orCreatePerModConfig = GetOrCreatePerModConfig(mod);
			return (category - 5) switch
			{
				1 => orCreatePerModConfig.Food.Value == Toggle.On, 
				2 => orCreatePerModConfig.Meads.Value == Toggle.On, 
				0 => orCreatePerModConfig.Feasts.Value == Toggle.On, 
				_ => false, 
			};
		}

		private static PerModCategoryConfig GetOrCreatePerModConfig(FoodSourceMod mod)
		{
			lock (DynamicConfigLock)
			{
				if (PerModConfigs.TryGetValue(mod.Id, out PerModCategoryConfig value))
				{
					return value;
				}
				if ((Object)(object)_instance == (Object)null)
				{
					throw new InvalidOperationException("Plugin instance is not initialized.");
				}
				string text = "ServingTray - " + SanitizeConfigText(mod.DisplayName) + " (" + SanitizeConfigText(mod.Id) + ")";
				PerModCategoryConfig perModCategoryConfig = new PerModCategoryConfig(_instance.config(text, "Food", Toggle.On, "If on, '" + mod.DisplayName + "' Food items go to 'Food - " + mod.DisplayName + "'. If off, they merge into vanilla Food.", 300), _instance.config(text, "Meads", Toggle.On, "If on, '" + mod.DisplayName + "' Mead items go to 'Meads - " + mod.DisplayName + "'. If off, they merge into vanilla Meads.", 200), _instance.config(text, "Feasts", Toggle.On, "If on, '" + mod.DisplayName + "' Feast items go to 'Feasts - " + mod.DisplayName + "'. If off, they merge into vanilla Feasts.", 100));
				PerModConfigs[mod.Id] = perModCategoryConfig;
				_pendingDynamicConfigSave = true;
				return perModCategoryConfig;
			}
		}

		internal static void FlushPendingDynamicConfigSave()
		{
			ServerSyncModTemplatePlugin instance = _instance;
			if (!((Object)(object)instance == (Object)null))
			{
				bool pendingDynamicConfigSave;
				lock (DynamicConfigLock)
				{
					pendingDynamicConfigSave = _pendingDynamicConfigSave;
					_pendingDynamicConfigSave = false;
				}
				if (pendingDynamicConfigSave)
				{
					instance.RunWithConfigAutoSaveDisabled(instance.SaveConfigWithoutWatcher);
				}
			}
		}

		private static string SanitizeConfigText(string text)
		{
			if (string.IsNullOrWhiteSpace(text))
			{
				return "Unknown";
			}
			char[] array = text.ToCharArray();
			for (int i = 0; i < array.Length; i++)
			{
				if (array[i] == '\r' || "=\n\t\\\"'[]".IndexOf(array[i]) >= 0 || char.IsControl(array[i]))
				{
					array[i] = '_';
				}
			}
			string text2 = new string(array).Trim();
			if (!string.IsNullOrWhiteSpace(text2))
			{
				return text2;
			}
			return "Unknown";
		}

		private ConfigEntry<T> config<T>(string group, string name, T value, string description, int order)
		{
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Expected O, but got Unknown
			return ((BaseUnityPlugin)this).Config.Bind<T>(group, name, value, new ConfigDescription(description, (AcceptableValueBase)null, new object[1]
			{
				new ConfigurationManagerAttributes
				{
					Order = order
				}
			}));
		}

		static ServerSyncModTemplatePlugin()
		{
			string configPath = Paths.ConfigPath;
			char directorySeparatorChar = Path.DirectorySeparatorChar;
			ConfigFileFullPath = configPath + directorySeparatorChar + ConfigFileName;
			ServerSyncModTemplateLogger = Logger.CreateLogSource("ServeYouRight");
			DynamicConfigLock = new object();
			ConfigReloadDelay = TimeSpan.FromSeconds(1.0);
			PerModConfigs = new Dictionary<string, PerModCategoryConfig>(StringComparer.OrdinalIgnoreCase);
		}
	}
	[HarmonyPatch(typeof(ObjectDB), "Awake")]
	public static class ObjectDbAwakePatch
	{
		private static void Postfix(ObjectDB __instance)
		{
			FeasterFoodInjector.Inject(__instance);
		}
	}
	[HarmonyPatch(typeof(ObjectDB), "CopyOtherDB")]
	public static class ObjectDbCopyOtherDbPatch
	{
		private static void Postfix(ObjectDB __instance)
		{
			FeasterFoodInjector.Inject(__instance);
		}
	}
	[HarmonyPatch(typeof(Player), "SetPlaceMode")]
	public static class PlayerSetPlaceModePatch
	{
		private static void Postfix(Player __instance, PieceTable buildPieces)
		{
			if (!((Object)(object)__instance != (Object)(object)Player.m_localPlayer) && !((Object)(object)buildPieces == (Object)null) && buildPieces.m_canRemoveFeasts)
			{
				FeasterFoodInjector.RefreshFoodPiecesAndMenu(ObjectDB.instance, buildPieces);
			}
		}
	}
	[HarmonyPatch(typeof(PieceTable), "UpdateAvailable")]
	public static class PieceTableUpdateAvailablePatch
	{
		private static void Postfix(PieceTable __instance)
		{
			if (!((Object)(object)__instance == (Object)null) && __instance.m_canRemoveFeasts)
			{
				FeasterFoodInjector.ApplyCustomCategoryLabels(__instance);
			}
		}
	}
	[HarmonyPatch(typeof(ItemDrop), "MakePiece")]
	public static class ItemDropMakePiecePatch
	{
		private static void Postfix(ItemDrop __instance)
		{
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)((Component)__instance).GetComponent<ServeYouRightInjectedPieceMarker>() == (Object)null)
			{
				return;
			}
			ParticleSystem[] componentsInChildren = ((Component)__instance).GetComponentsInChildren<ParticleSystem>(true);
			foreach (ParticleSystem obj in componentsInChildren)
			{
				obj.Stop(true, (ParticleSystemStopBehavior)0);
				obj.Clear(true);
				EmissionModule emission = obj.emission;
				((EmissionModule)(ref emission)).enabled = false;
				MainModule main = obj.main;
				((MainModule)(ref main)).playOnAwake = false;
				ParticleSystemRenderer component = ((Component)obj).GetComponent<ParticleSystemRenderer>();
				if ((Object)(object)component != (Object)null)
				{
					((Renderer)component).enabled = false;
				}
			}
		}
	}
	internal static class FeasterFoodInjector
	{
		private static bool _isInjecting;

		private static readonly Dictionary<string, ModCategoryInfo> ModCategoryCache = new Dictionary<string, ModCategoryInfo>(StringComparer.OrdinalIgnoreCase);

		private static readonly Dictionary<string, FoodSourceMod> SourceModHitCache = new Dictionary<string, FoodSourceMod>(StringComparer.OrdinalIgnoreCase);

		private static readonly object ModCategoryLock = new object();

		internal static void RefreshFoodPiecesAndMenu(ObjectDB? objectDb, PieceTable? buildPieces = null)
		{
			Inject(objectDb);
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer != (Object)null)
			{
				localPlayer.UpdateKnownRecipesList();
				localPlayer.UpdateAvailablePiecesList();
				ApplyCustomCategoryLabels(buildPieces ?? localPlayer.m_buildPieces);
			}
		}

		public static void Inject(ObjectDB? objectDb)
		{
			if (_isInjecting || (Object)(object)objectDb == (Object)null)
			{
				return;
			}
			_isInjecting = true;
			try
			{
				List<CandidateFood> candidateFoods = GetCandidateFoods(objectDb);
				if (candidateFoods.Count == 0)
				{
					return;
				}
				foreach (PieceTable feasterPieceTable in GetFeasterPieceTables(objectDb))
				{
					int num = InjectIntoTable(feasterPieceTable, candidateFoods);
					if (num > 0)
					{
						ServerSyncModTemplatePlugin.ServerSyncModTemplateLogger.LogInfo((object)$"Added {num} mod food pieces to feaster table '{((Object)feasterPieceTable).name}'.");
					}
					ApplyCustomCategoryLabels(feasterPieceTable);
				}
			}
			catch (Exception arg)
			{
				ServerSyncModTemplatePlugin.ServerSyncModTemplateLogger.LogError((object)$"Failed to inject feaster foods: {arg}");
			}
			finally
			{
				_isInjecting = false;
				ServerSyncModTemplatePlugin.FlushPendingDynamicConfigSave();
			}
		}

		private static List<CandidateFood> GetCandidateFoods(ObjectDB objectDb)
		{
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c0: Invalid comparison between Unknown and I4
			//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e4: Unknown result type (might be due to invalid IL or missing references)
			List<CandidateFood> list = new List<CandidateFood>();
			FeastRoutingData feastRoutingData = BuildFeastRoutingData(objectDb);
			foreach (GameObject item in objectDb.m_items)
			{
				if ((Object)(object)item == (Object)null)
				{
					continue;
				}
				ItemDrop component = item.GetComponent<ItemDrop>();
				SharedData val = component?.m_itemData?.m_shared;
				if (!((Object)(object)component == (Object)null) && val != null)
				{
					string prefabName = Utils.GetPrefabName(((Object)item).name);
					if (feastRoutingData.MaterialToResultPrefab.TryGetValue(prefabName, out GameObject value))
					{
						list.Add(new CandidateFood(component, value, (PieceCategory)5));
					}
					else if (!feastRoutingData.ResultPrefabNames.Contains(prefabName) && !((Object)(object)((Component)component).GetComponent<Feast>() != (Object)null) && (int)val.m_itemType == 2 && LooksLikeConsumableFood(val))
					{
						PieceCategory category = (PieceCategory)(val.m_isDrink ? 7 : 6);
						list.Add(new CandidateFood(component, ((Component)component).gameObject, category));
					}
				}
			}
			return list;
		}

		private static FeastRoutingData BuildFeastRoutingData(ObjectDB objectDb)
		{
			//IL_0248: Unknown result type (might be due to invalid IL or missing references)
			//IL_024e: Invalid comparison between Unknown and I4
			//IL_0187: Unknown result type (might be due to invalid IL or missing references)
			//IL_018d: Invalid comparison between Unknown and I4
			//IL_0168: Unknown result type (might be due to invalid IL or missing references)
			//IL_016e: Invalid comparison between Unknown and I4
			//IL_01b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bf: Invalid comparison between Unknown and I4
			Dictionary<string, ItemDrop> dictionary = new Dictionary<string, ItemDrop>(StringComparer.OrdinalIgnoreCase);
			foreach (GameObject item in objectDb.m_items)
			{
				if ((Object)(object)item == (Object)null)
				{
					continue;
				}
				ItemDrop component = item.GetComponent<ItemDrop>();
				if (!((Object)(object)component == (Object)null))
				{
					string prefabName = Utils.GetPrefabName(((Object)item).name);
					if (!dictionary.ContainsKey(prefabName))
					{
						dictionary[prefabName] = component;
					}
				}
			}
			Dictionary<string, GameObject> dictionary2 = new Dictionary<string, GameObject>(StringComparer.OrdinalIgnoreCase);
			HashSet<string> hashSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
			foreach (KeyValuePair<string, ItemDrop> item2 in dictionary)
			{
				string key = item2.Key;
				ItemDrop value = item2.Value;
				SharedData val = value.m_itemData?.m_shared;
				Feast component2 = ((Component)value).GetComponent<Feast>();
				if ((Object)(object)component2 == (Object)null || val == null)
				{
					continue;
				}
				ItemDrop foodItem = component2.m_foodItem;
				string text = (((Object)(object)foodItem != (Object)null) ? Utils.GetPrefabName(((Object)((Component)foodItem).gameObject).name) : string.Empty);
				bool flag = !string.IsNullOrWhiteSpace(text) && !string.Equals(text, key, StringComparison.OrdinalIgnoreCase);
				SharedData val2 = foodItem?.m_itemData?.m_shared;
				bool flag2 = (Object)(object)foodItem != (Object)null && ((Object)(object)((Component)foodItem).GetComponent<Feast>() != (Object)null || (val2 != null && ((int)val2.m_itemType == 2 || LooksLikeConsumableFood(val2))));
				if ((int)val.m_itemType == 1)
				{
					if (flag && flag2)
					{
						dictionary2[key] = ((Component)foodItem).gameObject;
						hashSet.Add(text);
					}
				}
				else if (flag && flag2 && (int)val.m_itemType != 2 && !LooksLikeConsumableFood(val))
				{
					dictionary2[key] = ((Component)foodItem).gameObject;
					hashSet.Add(text);
				}
				else
				{
					hashSet.Add(key);
				}
			}
			foreach (KeyValuePair<string, ItemDrop> item3 in dictionary)
			{
				string key2 = item3.Key;
				SharedData val3 = item3.Value.m_itemData?.m_shared;
				if (val3 == null || (int)val3.m_itemType != 1)
				{
					continue;
				}
				ItemDrop appendToolTip = val3.m_appendToolTip;
				if (!((Object)(object)appendToolTip == (Object)null))
				{
					string prefabName2 = Utils.GetPrefabName(((Object)((Component)appendToolTip).gameObject).name);
					if (!string.Equals(prefabName2, key2, StringComparison.OrdinalIgnoreCase) && (hashSet.Contains(prefabName2) || (Object)(object)((Component)appendToolTip).GetComponent<Feast>() != (Object)null))
					{
						dictionary2[key2] = ((Component)appendToolTip).gameObject;
						hashSet.Add(prefabName2);
					}
				}
			}
			return new FeastRoutingData(dictionary2, hashSet);
		}

		private static bool LooksLikeConsumableFood(SharedData shared)
		{
			if (!(shared.m_food > 0f) && !(shared.m_foodStamina > 0f) && !(shared.m_foodEitr > 0f))
			{
				return shared.m_isDrink;
			}
			return true;
		}

		private static IEnumerable<PieceTable> GetFeasterPieceTables(ObjectDB objectDb)
		{
			HashSet<PieceTable> uniqueTables = new HashSet<PieceTable>();
			foreach (GameObject item in objectDb.m_items)
			{
				if (!((Object)(object)item == (Object)null))
				{
					PieceTable val = item.GetComponent<ItemDrop>()?.m_itemData?.m_shared?.m_buildPieces;
					if (!((Object)(object)val == (Object)null) && val.m_canRemoveFeasts && (val.m_categories.Contains((PieceCategory)6) || val.m_categories.Contains((PieceCategory)7) || val.m_categories.Contains((PieceCategory)5)) && uniqueTables.Add(val))
					{
						yield return val;
					}
				}
			}
		}

		private static int InjectIntoTable(PieceTable table, List<CandidateFood> candidates)
		{
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			EnsureBaseCategories(table);
			HashSet<int> hashSet = BuildExistingFoodHashSet(table);
			int num = 0;
			foreach (CandidateFood candidate in candidates)
			{
				ItemDrop itemDrop = candidate.ItemDrop;
				GameObject placePrefab = candidate.PlacePrefab;
				int prefabNameHash = GetPrefabNameHash(((Component)itemDrop).gameObject);
				bool num2 = hashSet.Contains(prefabNameHash);
				bool flag = (Object)(object)placePrefab.GetComponent<ServeYouRightInjectedPieceMarker>() != (Object)null;
				if (!num2 || flag)
				{
					PieceCategory category = candidate.Category;
					PieceCategory category2 = ResolveTargetCategory(table, ((Component)itemDrop).gameObject, category);
					if (TryGetTemplate(table, category2, out Piece templatePiece, out WearNTear templateWearNTear) && PrepareFoodPrefab(placePrefab, itemDrop, category2, templatePiece, templateWearNTear) && !table.m_pieces.Contains(placePrefab))
					{
						table.m_pieces.Add(placePrefab);
						hashSet.Add(prefabNameHash);
						num++;
					}
				}
			}
			return num;
		}

		private static void EnsureBaseCategories(PieceTable table)
		{
			EnsureBaseCategoryExists(table, (PieceCategory)6, GetBaseCategoryLabel(table, (PieceCategory)6));
			EnsureBaseCategoryExists(table, (PieceCategory)7, GetBaseCategoryLabel(table, (PieceCategory)7));
			EnsureBaseCategoryExists(table, (PieceCategory)5, GetBaseCategoryLabel(table, (PieceCategory)5));
		}

		public static void ApplyCustomCategoryLabels(PieceTable? table)
		{
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)table == (Object)null)
			{
				return;
			}
			List<ModCategoryInfo> list;
			lock (ModCategoryLock)
			{
				list = ModCategoryCache.Values.ToList();
			}
			foreach (ModCategoryInfo item in list)
			{
				int num = table.m_categories.IndexOf(item.Category);
				if (num >= 0)
				{
					while (table.m_categoryLabels.Count <= num)
					{
						table.m_categoryLabels.Add(string.Empty);
					}
					string baseCategoryLabel = GetBaseCategoryLabel(table, item.BaseCategory);
					string text = ResolveCategoryDisplayLabel(item.BaseCategory, baseCategoryLabel);
					table.m_categoryLabels[num] = text + " - " + item.SourceMod.DisplayName;
				}
			}
		}

		private static bool TryGetTemplate(PieceTable table, PieceCategory category, out Piece templatePiece, out WearNTear templateWearNTear)
		{
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			templatePiece = null;
			templateWearNTear = null;
			Piece val = null;
			WearNTear val2 = null;
			foreach (GameObject piece in table.m_pieces)
			{
				if ((Object)(object)piece == (Object)null)
				{
					continue;
				}
				Piece component = piece.GetComponent<Piece>();
				if ((Object)(object)component == (Object)null || component.m_repairPiece || component.m_removePiece)
				{
					continue;
				}
				WearNTear component2 = piece.GetComponent<WearNTear>();
				if (!((Object)(object)component2 == (Object)null))
				{
					if (component.m_category == category)
					{
						templatePiece = component;
						templateWearNTear = component2;
						return true;
					}
					if ((Object)(object)val == (Object)null)
					{
						val = component;
						val2 = component2;
					}
				}
			}
			if ((Object)(object)val != (Object)null)
			{
				templatePiece = val;
				templateWearNTear = val2;
				return true;
			}
			ServerSyncModTemplatePlugin.ServerSyncModTemplateLogger.LogWarning((object)("No feaster template piece found for table '" + ((Object)table).name + "'."));
			return false;
		}

		private static bool PrepareFoodPrefab(GameObject foodPrefab, ItemDrop sourceItemDrop, PieceCategory category, Piece templatePiece, WearNTear templateWearNTear)
		{
			//IL_0098: Unknown result type (might be due to invalid IL or missing references)
			//IL_0099: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d7: Expected O, but got Unknown
			if ((Object)(object)foodPrefab.GetComponent<ZNetView>() == (Object)null)
			{
				ServerSyncModTemplatePlugin.ServerSyncModTemplateLogger.LogWarning((object)("Skipping '" + ((Object)foodPrefab).name + "' because it has no ZNetView."));
				return false;
			}
			Piece val = foodPrefab.GetComponent<Piece>();
			if ((Object)(object)val == (Object)null)
			{
				val = foodPrefab.AddComponent<Piece>();
				CopyPieceTemplate(templatePiece, val);
			}
			WearNTear component = foodPrefab.GetComponent<WearNTear>();
			if ((Object)(object)component == (Object)null)
			{
				component = foodPrefab.AddComponent<WearNTear>();
				CopyWearNTearTemplate(templateWearNTear, component);
			}
			SharedData shared = sourceItemDrop.m_itemData.m_shared;
			val.m_name = shared.m_name;
			val.m_description = shared.m_description;
			val.m_enabled = true;
			val.m_category = category;
			val.m_repairPiece = false;
			val.m_removePiece = false;
			val.m_resources = (Requirement[])(object)new Requirement[1]
			{
				new Requirement
				{
					m_resItem = sourceItemDrop,
					m_amount = 1,
					m_amountPerLevel = 1,
					m_recover = true
				}
			};
			Sprite[] icons = shared.m_icons;
			if (icons != null && icons.Length > 0)
			{
				val.m_icon = sourceItemDrop.m_itemData.GetIcon();
			}
			if ((Object)(object)foodPrefab.GetComponent<ServeYouRightInjectedPieceMarker>() == (Object)null)
			{
				foodPrefab.AddComponent<ServeYouRightInjectedPieceMarker>();
			}
			return true;
		}

		private static void CopyPieceTemplate(Piece source, Piece destination)
		{
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bb: Unknown result type (might be due to invalid IL or missing references)
			destination.m_targetNonPlayerBuilt = source.m_targetNonPlayerBuilt;
			destination.m_icon = source.m_icon;
			destination.m_isUpgrade = source.m_isUpgrade;
			destination.m_comfort = source.m_comfort;
			destination.m_comfortGroup = source.m_comfortGroup;
			destination.m_comfortObject = source.m_comfortObject;
			destination.m_groundPiece = source.m_groundPiece;
			destination.m_allowAltGroundPlacement = source.m_allowAltGroundPlacement;
			destination.m_groundOnly = source.m_groundOnly;
			destination.m_cultivatedGroundOnly = source.m_cultivatedGroundOnly;
			destination.m_waterPiece = source.m_waterPiece;
			destination.m_clipGround = source.m_clipGround;
			destination.m_clipEverything = source.m_clipEverything;
			destination.m_noInWater = source.m_noInWater;
			destination.m_notOnWood = source.m_notOnWood;
			destination.m_notOnTiltingSurface = source.m_notOnTiltingSurface;
			destination.m_inCeilingOnly = source.m_inCeilingOnly;
			destination.m_notOnFloor = source.m_notOnFloor;
			destination.m_noClipping = source.m_noClipping;
			destination.m_onlyInTeleportArea = source.m_onlyInTeleportArea;
			destination.m_allowedInDungeons = source.m_allowedInDungeons;
			destination.m_spaceRequirement = source.m_spaceRequirement;
			destination.m_canRotate = source.m_canRotate;
			destination.m_randomInitBuildRotation = source.m_randomInitBuildRotation;
			destination.m_canBeRemoved = source.m_canBeRemoved;
			destination.m_canRockJade = source.m_canRockJade;
			destination.m_allowRotatedOverlap = source.m_allowRotatedOverlap;
			destination.m_vegetationGroundOnly = source.m_vegetationGroundOnly;
			destination.m_blockingPieces = ((source.m_blockingPieces == null) ? null : new List<Piece>(source.m_blockingPieces));
			destination.m_blockRadius = source.m_blockRadius;
			destination.m_mustConnectTo = source.m_mustConnectTo;
			destination.m_connectRadius = source.m_connectRadius;
			destination.m_mustBeAboveConnected = source.m_mustBeAboveConnected;
			destination.m_noVines = source.m_noVines;
			destination.m_extraPlacementDistance = source.m_extraPlacementDistance;
			destination.m_onlyInBiome = source.m_onlyInBiome;
			destination.m_harvest = source.m_harvest;
			destination.m_harvestRadius = source.m_harvestRadius;
			destination.m_harvestRadiusMaxLevel = source.m_harvestRadiusMaxLevel;
			destination.m_placeEffect = source.m_placeEffect;
			destination.m_dlc = source.m_dlc;
			destination.m_craftingStation = source.m_craftingStation;
			destination.m_returnResourceHeightOffset = source.m_returnResourceHeightOffset;
			destination.m_destroyedLootPrefab = source.m_destroyedLootPrefab;
		}

		private static void CopyWearNTearTemplate(WearNTear source, WearNTear destination)
		{
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_009e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ef: Unknown result type (might be due to invalid IL or missing references)
			destination.m_onDestroyed = source.m_onDestroyed;
			destination.m_onDamaged = source.m_onDamaged;
			destination.m_new = source.m_new;
			destination.m_worn = source.m_worn;
			destination.m_broken = source.m_broken;
			destination.m_wet = source.m_wet;
			destination.m_noRoofWear = source.m_noRoofWear;
			destination.m_noSupportWear = source.m_noSupportWear;
			destination.m_ashDamageImmune = source.m_ashDamageImmune;
			destination.m_ashDamageResist = source.m_ashDamageResist;
			destination.m_burnable = source.m_burnable;
			destination.m_materialType = source.m_materialType;
			destination.m_supports = source.m_supports;
			destination.m_comOffset = source.m_comOffset;
			destination.m_forceCorrectCOMCalculation = source.m_forceCorrectCOMCalculation;
			destination.m_staticPosition = source.m_staticPosition;
			destination.m_nonSolidRenderers = ((source.m_nonSolidRenderers == null) ? null : new List<Renderer>(source.m_nonSolidRenderers));
			destination.m_health = source.m_health;
			destination.m_damages = source.m_damages;
			destination.m_minToolTier = source.m_minToolTier;
			destination.m_hitNoise = source.m_hitNoise;
			destination.m_destroyNoise = source.m_destroyNoise;
			destination.m_triggerPrivateArea = source.m_triggerPrivateArea;
			destination.m_destroyedEffect = source.m_destroyedEffect;
			destination.m_hitEffect = source.m_hitEffect;
			destination.m_switchEffect = source.m_switchEffect;
			destination.m_autoCreateFragments = source.m_autoCreateFragments;
			destination.m_fragmentRoots = ((source.m_fragmentRoots == null) ? null : ((GameObject[])source.m_fragmentRoots.Clone()));
		}

		private static HashSet<int> BuildExistingFoodHashSet(PieceTable table)
		{
			HashSet<int> hashSet = new HashSet<int>();
			foreach (GameObject piece in table.m_pieces)
			{
				if ((Object)(object)piece == (Object)null)
				{
					continue;
				}
				ItemDrop component = piece.GetComponent<ItemDrop>();
				if ((Object)(object)component == (Object)null)
				{
					continue;
				}
				hashSet.Add(GetPrefabNameHash(((Component)component).gameObject));
				if (!((Object)(object)piece.GetComponent<Feast>() != (Object)null) && !((Object)(object)piece.GetComponent<ServeYouRightInjectedPieceMarker>() != (Object)null))
				{
					continue;
				}
				Requirement[] array = piece.GetComponent<Piece>()?.m_resources ?? Array.Empty<Requirement>();
				foreach (Requirement val in array)
				{
					if ((Object)(object)val?.m_resItem != (Object)null)
					{
						hashSet.Add(GetPrefabNameHash(((Component)val.m_resItem).gameObject));
					}
				}
			}
			return hashSet;
		}

		private static PieceCategory ResolveTargetCategory(PieceTable table, GameObject foodPrefab, PieceCategory baseCategory)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			string baseCategoryLabel = GetBaseCategoryLabel(table, baseCategory);
			EnsureBaseCategoryExists(table, baseCategory, baseCategoryLabel);
			if (!TryResolveFoodSourceMod(foodPrefab, out var sourceMod))
			{
				return baseCategory;
			}
			if (!ServerSyncModTemplatePlugin.UseModSpecificTab(sourceMod, baseCategory))
			{
				return baseCategory;
			}
			return ResolveOrCreateModCategory(baseCategory, sourceMod);
		}

		private static string GetBaseCategoryLabel(PieceTable table, PieceCategory category)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			int num = table.m_categories.IndexOf(category);
			if (num >= 0 && num < table.m_categoryLabels.Count)
			{
				string text = table.m_categoryLabels[num];
				if (!string.IsNullOrWhiteSpace(text))
				{
					return text;
				}
			}
			return GetFallbackCategoryLabel(category);
		}

		private static PieceCategory ResolveOrCreateModCategory(PieceCategory baseCategory, FoodSourceMod sourceMod)
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Expected I4, but got Unknown
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_0092: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
			string key = $"{sourceMod.Id}:{(int)baseCategory}";
			lock (ModCategoryLock)
			{
				if (ModCategoryCache.TryGetValue(key, out ModCategoryInfo value))
				{
					return value.Category;
				}
				string text = BuildStableCategoryKey(baseCategory, sourceMod.Id);
				if (!JotunnBridge.TryAddPieceCategory(text, out var category))
				{
					ServerSyncModTemplatePlugin.ServerSyncModTemplateLogger.LogWarning((object)$"Could not create Jotunn category '{text}'. Falling back to vanilla category '{baseCategory}'.");
					return baseCategory;
				}
				ModCategoryCache[key] = new ModCategoryInfo(category, baseCategory, sourceMod);
				return category;
			}
		}

		private static string ResolveCategoryDisplayLabel(PieceCategory category, string baseLabel)
		{
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			if (!string.IsNullOrWhiteSpace(baseLabel))
			{
				string text = baseLabel.Trim();
				string text2 = text;
				if (LooksLikeLocalizationTokenKey(text) && !text.StartsWith("$", StringComparison.Ordinal))
				{
					text2 = "$" + text;
				}
				if (Localization.instance != null)
				{
					string text3 = Localization.instance.Localize(text2);
					if (!string.IsNullOrWhiteSpace(text3) && !string.Equals(text3, text2, StringComparison.Ordinal))
					{
						return text3.Trim();
					}
				}
				if (LooksLikeLocalizationTokenKey(text))
				{
					return GetFallbackCategoryLabel(category);
				}
				return text;
			}
			return GetFallbackCategoryLabel(category);
		}

		private static bool LooksLikeLocalizationTokenKey(string value)
		{
			if (string.IsNullOrWhiteSpace(value))
			{
				return false;
			}
			if (value.StartsWith("$", StringComparison.Ordinal))
			{
				return true;
			}
			if (value.IndexOfAny(new char[4] { ' ', '\t', '\r', '\n' }) >= 0)
			{
				return false;
			}
			if (!value.Contains("_"))
			{
				return false;
			}
			foreach (char c in value)
			{
				if (!char.IsLetterOrDigit(c) && c != '_' && c != '.' && c != '-')
				{
					return false;
				}
			}
			return true;
		}

		private static string BuildStableCategoryKey(PieceCategory baseCategory, string modId)
		{
			//IL_0000: 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)
			//IL_0014: Expected I4, but got Unknown
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Expected I4, but got Unknown
			string arg = (baseCategory - 5) switch
			{
				1 => "food", 
				2 => "meads", 
				0 => "feasts", 
				_ => $"cat{(int)baseCategory}", 
			};
			int num = StringExtensionMethods.GetStableHashCode(modId ?? string.Empty);
			if (num == int.MinValue)
			{
				num = int.MaxValue;
			}
			num = Math.Abs(num);
			return $"syr_{arg}_{num}";
		}

		private unsafe static string GetFallbackCategoryLabel(PieceCategory category)
		{
			//IL_0000: 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)
			//IL_0014: Expected I4, but got Unknown
			return (category - 5) switch
			{
				1 => "Food", 
				2 => "Meads", 
				0 => "Feasts", 
				_ => ((object)(*(PieceCategory*)(&category))/*cast due to .constrained prefix*/).ToString(), 
			};
		}

		private static bool TryResolveFoodSourceMod(GameObject foodPrefab, out FoodSourceMod sourceMod)
		{
			string prefabName = Utils.GetPrefabName(((Object)foodPrefab).name);
			if (SourceModHitCache.TryGetValue(prefabName, out var value))
			{
				sourceMod = value;
				return true;
			}
			if (KnownCloneSourceBridge.TryResolveSourceMod(prefabName, out var sourceMod2))
			{
				sourceMod = sourceMod2;
				SourceModHitCache[prefabName] = sourceMod;
				return true;
			}
			if (JotunnBridge.TryGetPrefabSourceMod(prefabName, out var sourceMod3))
			{
				sourceMod = sourceMod3;
				SourceModHitCache[prefabName] = sourceMod;
				return true;
			}
			sourceMod = default(FoodSourceMod);
			return false;
		}

		private static void EnsureBaseCategoryExists(PieceTable table, PieceCategory category, string label)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			if (table.m_categories.IndexOf(category) < 0)
			{
				table.m_categories.Add(category);
				while (table.m_categoryLabels.Count < table.m_categories.Count - 1)
				{
					table.m_categoryLabels.Add(((object)table.m_categories[table.m_categoryLabels.Count]/*cast due to .constrained prefix*/).ToString());
				}
				table.m_categoryLabels.Add(label);
			}
		}

		private static int GetPrefabNameHash(GameObject prefab)
		{
			return StringExtensionMethods.GetStableHashCode(Utils.GetPrefabName(((Object)prefab).name));
		}
	}
	internal readonly struct FoodSourceMod
	{
		public string Id { get; }

		public string DisplayName { get; }

		public FoodSourceMod(string id, string displayName)
		{
			Id = id;
			DisplayName = displayName;
		}
	}
	internal readonly struct CandidateFood
	{
		public ItemDrop ItemDrop { get; }

		public GameObject PlacePrefab { get; }

		public PieceCategory Category { get; }

		public CandidateFood(ItemDrop itemDrop, GameObject placePrefab, PieceCategory category)
		{
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			ItemDrop = itemDrop;
			PlacePrefab = placePrefab;
			Category = category;
		}
	}
	internal sealed class FeastRoutingData
	{
		public Dictionary<string, GameObject> MaterialToResultPrefab { get; }

		public HashSet<string> ResultPrefabNames { get; }

		public FeastRoutingData(Dictionary<string, GameObject> materialToResultPrefab, HashSet<string> resultPrefabNames)
		{
			MaterialToResultPrefab = materialToResultPrefab;
			ResultPrefabNames = resultPrefabNames;
		}
	}
	internal sealed class ModCategoryInfo
	{
		public PieceCategory Category { get; }

		public PieceCategory BaseCategory { get; }

		public FoodSourceMod SourceMod { get; }

		public ModCategoryInfo(PieceCategory category, PieceCategory baseCategory, FoodSourceMod sourceMod)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			Category = category;
			BaseCategory = baseCategory;
			SourceMod = sourceMod;
		}
	}
	internal sealed class PerModCategoryConfig
	{
		public ConfigEntry<ServerSyncModTemplatePlugin.Toggle> Food { get; }

		public ConfigEntry<ServerSyncModTemplatePlugin.Toggle> Meads { get; }

		public ConfigEntry<ServerSyncModTemplatePlugin.Toggle> Feasts { get; }

		public PerModCategoryConfig(ConfigEntry<ServerSyncModTemplatePlugin.Toggle> food, ConfigEntry<ServerSyncModTemplatePlugin.Toggle> meads, ConfigEntry<ServerSyncModTemplatePlugin.Toggle> feasts)
		{
			Food = food;
			Meads = meads;
			Feasts = feasts;
		}
	}
	internal static class KnownCloneSourceBridge
	{
		private const string WackyGuid = "WackyMole.WackysDatabase";

		private const string DefaultWackyName = "WackysDatabase";

		private static bool _initialized;

		private static MethodInfo? _wackyGetClonedMap;

		private static string _wackyDisplayName = "WackysDatabase";

		private static readonly HashSet<string> WackyCloneHitCache = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

		public static bool TryResolveSourceMod(string prefabName, out FoodSourceMod sourceMod)
		{
			sourceMod = default(FoodSourceMod);
			EnsureInitialized();
			if (_wackyGetClonedMap == null)
			{
				return false;
			}
			if (WackyCloneHitCache.Contains(prefabName))
			{
				sourceMod = new FoodSourceMod("WackyMole.WackysDatabase", _wackyDisplayName);
				return true;
			}
			try
			{
				if (string.IsNullOrWhiteSpace(_wackyGetClonedMap.Invoke(null, new object[1] { prefabName }) as string))
				{
					return false;
				}
				WackyCloneHitCache.Add(prefabName);
				sourceMod = new FoodSourceMod("WackyMole.WackysDatabase", _wackyDisplayName);
				return true;
			}
			catch (Exception ex)
			{
				ServerSyncModTemplatePlugin.ServerSyncModTemplateLogger.LogDebug((object)("Known clone source resolution failed for '" + prefabName + "': " + ex.Message));
				return false;
			}
		}

		private static void EnsureInitialized()
		{
			if (_initialized)
			{
				return;
			}
			_initialized = true;
			try
			{
				_wackyGetClonedMap = Type.GetType("API.WackyAPI, WackysDatabase")?.GetMethod("GetClonedMap", BindingFlags.Static | BindingFlags.Public, null, new Type[1] { typeof(string) }, null);
				if (Chainloader.PluginInfos.TryGetValue("WackyMole.WackysDatabase", out var value))
				{
					object obj;
					if (value == null)
					{
						obj = null;
					}
					else
					{
						BepInPlugin metadata = value.Metadata;
						obj = ((metadata != null) ? metadata.Name : null);
					}
					if (obj == null)
					{
						obj = string.Empty;
					}
					string text = (string)obj;
					if (!string.IsNullOrWhiteSpace(text))
					{
						_wackyDisplayName = text;
					}
				}
			}
			catch (Exception ex)
			{
				_wackyGetClonedMap = null;
				ServerSyncModTemplatePlugin.ServerSyncModTemplateLogger.LogDebug((object)("Known clone source bridge init failed: " + ex.Message));
			}
		}
	}
	internal static class JotunnBridge
	{
		private static bool _initialized;

		private static bool _available;

		private static MethodInfo? _modQueryEnable;

		private static MethodInfo? _modQueryGetPrefab;

		private static PropertyInfo? _pieceManagerInstance;

		private static MethodInfo? _pieceManagerAddPieceCategory;

		public static void InitializeAndEnableModQuery()
		{
			if (_initialized)
			{
				EnableModQuery();
				return;
			}
			_initialized = true;
			try
			{
				Type? type = Type.GetType("Jotunn.Utils.ModQuery, Jotunn");
				Type type2 = Type.GetType("Jotunn.Managers.PieceManager, Jotunn");
				_modQueryEnable = type?.GetMethod("Enable", BindingFlags.Static | BindingFlags.Public);
				_modQueryGetPrefab = type?.GetMethod("GetPrefab", BindingFlags.Static | BindingFlags.Public, null, new Type[1] { typeof(string) }, null);
				_pieceManagerInstance = type2?.GetProperty("Instance", BindingFlags.Static | BindingFlags.Public);
				_pieceManagerAddPieceCategory = type2?.GetMethod("AddPieceCategory", BindingFlags.Instance | BindingFlags.Public, null, new Type[1] { typeof(string) }, null);
				_available = _modQueryEnable != null && _modQueryGetPrefab != null && _pieceManagerInstance != null && _pieceManagerAddPieceCategory != null;
			}
			catch (Exception ex)
			{
				_available = false;
				ServerSyncModTemplatePlugin.ServerSyncModTemplateLogger.LogWarning((object)("Failed to initialize Jotunn bridge: " + ex.Message));
			}
			if (!_available)
			{
				ServerSyncModTemplatePlugin.ServerSyncModTemplateLogger.LogWarning((object)"Jotunn bridge is unavailable. Mod-specific tabs will fall back to vanilla categories.");
			}
			else
			{
				EnableModQuery();
			}
		}

		public static bool TryGetPrefabSourceMod(string prefabName, out FoodSourceMod sourceMod)
		{
			sourceMod = default(FoodSourceMod);
			if (!_available || _modQueryGetPrefab == null)
			{
				return false;
			}
			try
			{
				object obj = _modQueryGetPrefab.Invoke(null, new object[1] { prefabName });
				if (obj == null)
				{
					return false;
				}
				object obj2 = obj.GetType().GetProperty("SourceMod", BindingFlags.Instance | BindingFlags.Public)?.GetValue(obj);
				if (obj2 == null)
				{
					return false;
				}
				BepInPlugin val = (BepInPlugin)((obj2 is BepInPlugin) ? obj2 : null);
				if (val != null)
				{
					string displayName = (string.IsNullOrWhiteSpace(val.Name) ? val.GUID : val.Name);
					sourceMod = new FoodSourceMod(val.GUID, displayName);
					return true;
				}
				object obj3 = obj2.GetType().GetProperty("GUID", BindingFlags.Instance | BindingFlags.Public)?.GetValue(obj2) as string;
				string text = obj2.GetType().GetProperty("Name", BindingFlags.Instance | BindingFlags.Public)?.GetValue(obj2) as string;
				if (obj3 == null)
				{
					obj3 = string.Empty;
				}
				string text2 = (string)obj3;
				if (string.IsNullOrWhiteSpace(text2))
				{
					return false;
				}
				string displayName2 = (string.IsNullOrWhiteSpace(text) ? text2 : text);
				sourceMod = new FoodSourceMod(text2, displayName2);
				return true;
			}
			catch (Exception ex)
			{
				ServerSyncModTemplatePlugin.ServerSyncModTemplateLogger.LogDebug((object)("ModQuery.GetPrefab failed for '" + prefabName + "': " + ex.Message));
				return false;
			}
		}

		public static bool TryAddPieceCategory(string categoryName, out PieceCategory category)
		{
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Expected I4, but got Unknown
			category = (PieceCategory)6;
			if (!_available || _pieceManagerInstance == null || _pieceManagerAddPieceCategory == null)
			{
				return false;
			}
			try
			{
				object value = _pieceManagerInstance.GetValue(null);
				if (value == null)
				{
					return false;
				}
				object obj = _pieceManagerAddPieceCategory.Invoke(value, new object[1] { categoryName });
				if (obj is PieceCategory val)
				{
					category = (PieceCategory)(int)val;
					return true;
				}
				if (obj is int num)
				{
					category = (PieceCategory)num;
					return true;
				}
				return false;
			}
			catch (Exception ex)
			{
				ServerSyncModTemplatePlugin.ServerSyncModTemplateLogger.LogWarning((object)("Failed to add Jotunn piece category '" + categoryName + "': " + ex.Message));
				return false;
			}
		}

		private static void EnableModQuery()
		{
			if (!_available || _modQueryEnable == null)
			{
				return;
			}
			try
			{
				_modQueryEnable.Invoke(null, null);
			}
			catch (Exception ex)
			{
				ServerSyncModTemplatePlugin.ServerSyncModTemplateLogger.LogWarning((object)("Failed to enable Jotunn ModQuery: " + ex.Message));
			}
		}
	}
	internal sealed class ServeYouRightInjectedPieceMarker : MonoBehaviour
	{
	}
}