Decompiled source of HIJOS DE FREYJA ROLEPLAY v0.0.6

Bepinex/plugins/CraftByProfession/CraftByProfession.dll

Decompiled 3 weeks ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Serialization;
using System.Runtime.Versioning;
using System.Text;
using System.Threading.Tasks;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using CraftByProfession.Loaders;
using CraftByProfession.Managers;
using CraftByProfession.Patches;
using CraftByProfession.UISelection;
using HarmonyLib;
using JetBrains.Annotations;
using Microsoft.CodeAnalysis;
using ServerSync;
using TMPro;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: AssemblyTitle("CraftByProfession")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("CraftByProfession")]
[assembly: AssemblyCopyright("Copyright ©  2024")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("9d7b5d97-1586-4682-8c7b-b1174633c18e")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace CraftByProfession
{
	internal class ConfigurationFile
	{
		private static ConfigEntry<bool> _serverConfigLocked = null;

		public static ConfigEntry<bool> debug;

		public static string path_to_your_mod_folder;

		public static ConfigEntry<KeyCode> compendiumHotKey;

		public static ConfigEntry<bool> bypassAdmins;

		public static ConfigEntry<bool> showAllInCompendium;

		public static ConfigEntry<string> professions;

		public static ConfigEntry<KeyCode> professionsHotKey;

		public static ConfigEntry<string> profession;

		public static ConfigEntry<string> professionsSprites;

		public static ConfigEntry<bool> allowProfessionChange;

		public static ConfigEntry<string> beehiveProfession;

		public static ConfigEntry<string> beehiveUsageForbidden;

		public static ConfigEntry<string> fermenterProfession;

		public static ConfigEntry<string> fermenterUsageForbidden;

		public static ConfigEntry<string> smelterProfession;

		public static ConfigEntry<string> smelterUsageForbidden;

		public static ConfigEntry<string> blastfurnaceProfession;

		public static ConfigEntry<string> blastfurnaceUsageForbidden;

		public static ConfigEntry<string> eitrrefineryProfession;

		public static ConfigEntry<string> eitrrefineryUsageForbidden;

		public static ConfigEntry<string> actions;

		public static ConfigEntry<string> unavailablePieceMessage;

		public static ConfigEntry<string> unavailablePieceLevelMessage;

		public static ConfigEntry<string> unavailableRecipeMessage;

		public static ConfigEntry<string> unavailableRecipeLevelMessage;

		public static ConfigEntry<string> unremovablePieceMessage;

		public static ConfigEntry<string> unremovablePieceLevelMessage;

		public static ConfigFile configFile;

		private static readonly string ConfigFileName = "Turbero.ValheimProfessionRoleplay.cfg";

		private static readonly string ConfigFileFullPath;

		private static readonly ConfigSync ConfigSync;

		internal static void LoadConfig(BaseUnityPlugin plugin)
		{
			configFile = plugin.Config;
			_serverConfigLocked = config("1 - General", "Lock Configuration", value: true, "If on, the configuration is locked and can be changed by server admins only.");
			ConfigSync.AddLockingConfigEntry<bool>(_serverConfigLocked);
			string text = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) + "\\professions";
			path_to_your_mod_folder = text;
			debug = config("1 - General", "DebugMode", value: false, "Enabling/Disabling the debugging in the console (default = false)", synchronizedSetting: false);
			compendiumHotKey = config<KeyCode>("2 - Config", "CompendiumHotKey", (KeyCode)284, "Hot key to open the compendium (default = F3)", synchronizedSetting: false);
			bypassAdmins = config("2 - Config", "BypassAdmins", value: false, "Admins ignore profession conditions (default = true)");
			showAllInCompendium = config("2 - Config", "ShowAllInCompendium", value: true, "Shows all objects your profession can manage in Compendium or only those you have learnt until now (default = false)");
			professionsHotKey = config<KeyCode>("3 - Assignments", "ProfessionsHotKey", (KeyCode)112, "Hoy key for professions panel", synchronizedSetting: false);
			professions = config("3 - Assignments", "Professions", "", "Comma-separated list of professions.");
			professionsSprites = config("3 - Assignments", "ProfessionsSprites", "", "Comma-separated list of sprites for professions.");
			allowProfessionChange = config("3 - Assignments", "AllowProfessionChange", value: false, "Allow changing professions after selecting for first time (default = false).");
			beehiveProfession = config("4 - Exclusive pieces", "BeehiveProfession", "", "Exclusive profession for using the beehive. Leave blank to allow to everyone to use it.");
			beehiveUsageForbidden = config("4 - Exclusive pieces", "BeehiveUsageForbidden", "You can't use the beehive with your current profession!", "Message to show when beehive cannot be used");
			fermenterProfession = config("4 - Exclusive pieces", "FermenterProfession", "", "Exclusive profession for using the fermenter. Leave blank to allow to everyone to use it.");
			fermenterUsageForbidden = config("4 - Exclusive pieces", "FermenterUsageForbidden", "You can't use the fermenter with your current profession!", "Message to show when the fermenter cannot be used");
			smelterProfession = config("4 - Exclusive pieces", "SmelterProfession", "", "Exclusive profession for using the smelter. Leave blank to allow to everyone to use it.");
			smelterUsageForbidden = config("4 - Exclusive pieces", "SmelterUsageForbidden", "You can't use the smelter with your current profession!", "Message to show when the smelter cannot be used");
			blastfurnaceProfession = config("4 - Exclusive pieces", "BlastFurnaceProfession", "", "Exclusive profession for using the blast furnace. Leave blank to allow to everyone to use it.");
			blastfurnaceUsageForbidden = config("4 - Exclusive pieces", "BlastfurnaceUsageForbidden", "You can't use the blast furnace with your current profession!", "Message to show when the blast furnace cannot be used");
			eitrrefineryProfession = config("4 - Exclusive pieces", "EitrRefineryProfession", "", "Exclusive profession for using the eitr refinery. Leave blank to allow to everyone to use it.");
			eitrrefineryUsageForbidden = config("4 - Exclusive pieces", "EitrRefineryUsageForbidden", "You can't use the eitr refinery with your current profession!", "Message to show when the eitr refinery cannot be used");
			profession = config("5 - Language", "Profession", "Profession", "Profession.");
			actions = config("5 - Language", "Actions", "Actions", "Actions section in compendium.");
			unavailablePieceMessage = config("5 - Language", "UnavailablePieceMessage", "You don't have the profession {0} to place that object", "Message to show when piece cannot be placed ({0} = profession)");
			unavailablePieceLevelMessage = config("5 - Language", "UnavailablePieceLevelMessage", "You don't have the necessary skill level {0} in {1} to place that object", "Message to show when piece cannot be placed ({0} = level, {1} = profession)");
			unavailableRecipeMessage = config("5 - Language", "UnavailableRecipeMessage", "You don't have the profession {0} to create that object", "Message to show when recipe cannot be crafted ({0} = profession)");
			unavailableRecipeLevelMessage = config("5 - Language", "UnavailableRecipeLevelMessage", "You don't have the necessary skill level {0} in {1} to create that object", "Message to show when recipe cannot be crafted ({0} = level, {1} = profession)");
			unremovablePieceMessage = config("5 - Language", "UnremovablePieceMessage", "You don't have the profession {0} to remove that object", "Message to show when piece cannot be removed ({0} = profession)");
			unremovablePieceLevelMessage = config("5 - Language", "UnremovablePieceLevelMessage", "You don't have the necessary skill level {0} in {1} to remove that object", "Message to show when piece cannot be removed ({0} = level, {1} = profession");
			bypassAdmins.SettingChanged += OnSettingChanged;
			showAllInCompendium.SettingChanged += OnSettingChanged;
			professions.SettingChanged += OnSettingChanged;
			professionsSprites.SettingChanged += OnSettingChanged;
			allowProfessionChange.SettingChanged += OnSettingChanged;
			SetupWatcher();
		}

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

		private static void ReadConfigValues(object sender, FileSystemEventArgs e)
		{
			if (!File.Exists(ConfigFileFullPath))
			{
				return;
			}
			try
			{
				Logger.Log("Attempting to reload configuration...");
				configFile.Reload();
			}
			catch
			{
				Logger.LogError("There was an issue loading " + ConfigFileName);
			}
		}

		private static void OnSettingChanged(object sender, EventArgs e)
		{
			if ((Object)(object)ZNetScene.instance != (Object)null)
			{
				LoaderPiecePatch.Postfix(ZNetScene.instance);
				LoaderRecipePatch.Postfix(ZNetScene.instance);
			}
			CraftStationDescriptionsPatch.updated = false;
			if ((Object)(object)ProfessionUI.professionUIResetButton != (Object)null)
			{
				((Component)ProfessionUI.professionUIResetButton).gameObject.SetActive(bypassAdmins.Value || allowProfessionChange.Value);
			}
		}

		private static ConfigEntry<T> config<T>(string group, string name, T value, string description, bool synchronizedSetting = true)
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Expected O, but got Unknown
			return config(group, name, value, new ConfigDescription(description, (AcceptableValueBase)null, Array.Empty<object>()), synchronizedSetting);
		}

		private static ConfigEntry<T> config<T>(string group, string name, T value, ConfigDescription description, bool synchronizedSetting = true)
		{
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Expected O, but got Unknown
			ConfigDescription val = new ConfigDescription(description.Description + (synchronizedSetting ? " [Synced with Server]" : " [Not Synced with Server]"), description.AcceptableValues, description.Tags);
			ConfigEntry<T> val2 = configFile.Bind<T>(group, name, value, val);
			SyncedConfigEntry<T> syncedConfigEntry = ConfigSync.AddConfigEntry<T>(val2);
			syncedConfigEntry.SynchronizedConfig = synchronizedSetting;
			return val2;
		}

		static ConfigurationFile()
		{
			string configPath = Paths.ConfigPath;
			char directorySeparatorChar = Path.DirectorySeparatorChar;
			ConfigFileFullPath = configPath + directorySeparatorChar + ConfigFileName;
			ConfigSync = new ConfigSync("Turbero.ValheimProfessionRoleplay")
			{
				DisplayName = "Valheim Profession Roleplay",
				CurrentVersion = "1.0.0",
				MinimumRequiredVersion = "1.0.0"
			};
		}
	}
	public static class Logger
	{
		public static ManualLogSource logger = Logger.CreateLogSource("Valheim Profession Roleplay");

		internal static void Log(object s)
		{
			if (ConfigurationFile.debug.Value)
			{
				logger.LogInfo((object)s?.ToString());
			}
		}

		internal static void LogInfo(object s)
		{
			logger.LogInfo((object)s?.ToString());
		}

		internal static void LogWarning(object s)
		{
			logger.LogWarning((object)s?.ToString());
		}

		internal static void LogError(object s)
		{
			logger.LogError((object)s?.ToString());
		}
	}
	public class ProfessionData
	{
		public string professionName;

		public string ObjectName { get; set; }

		public string ItemName { get; set; }

		public SkillType SkillType { get; set; } = (SkillType)0;


		public int MinSkillLevel { get; set; } = 0;


		public override string ToString()
		{
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			return $"[objectName: {ObjectName}, itemName: {ItemName}, professionName: {professionName}, skillType: {SkillType}, minSkillLevel: {MinSkillLevel}]";
		}
	}
	public class ProfessionDataComponent : MonoBehaviour
	{
		public string objectName;

		public string itemName;

		public string professionName;

		public SkillType skillType = (SkillType)0;

		public int minSkillLevel = 0;

		public ProfessionData getProfessionData()
		{
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			return new ProfessionData
			{
				ObjectName = objectName,
				ItemName = itemName,
				professionName = professionName,
				SkillType = skillType,
				MinSkillLevel = minSkillLevel
			};
		}

		public override string ToString()
		{
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			return $"[objectName: {objectName}, itemName: {itemName}, professionName: {professionName}, skillType: {skillType}, minSkillLevel: {minSkillLevel}]";
		}
	}
	[BepInPlugin("Turbero.ValheimProfessionRoleplay", "Valheim Profession Roleplay", "1.0.0")]
	public class CraftByProfession : BaseUnityPlugin
	{
		public const string GUID = "Turbero.ValheimProfessionRoleplay";

		public const string NAME = "Valheim Profession Roleplay";

		public const string VERSION = "1.0.0";

		private readonly Harmony harmony = new Harmony("Turbero.ValheimProfessionRoleplay");

		private void Awake()
		{
			ConfigurationFile.LoadConfig((BaseUnityPlugin)(object)this);
			harmony.PatchAll();
		}

		private void onDestroy()
		{
			ConfigurationFile.configFile.Save();
			harmony.UnpatchSelf();
		}

		public void Update()
		{
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d5: Unknown result type (might be due to invalid IL or missing references)
			if (!Object.op_Implicit((Object)(object)Player.m_localPlayer) || !Object.op_Implicit((Object)(object)InventoryGui.instance))
			{
				return;
			}
			if (Input.GetKeyDown((KeyCode)27) || Input.GetKeyDown((KeyCode)9) || ((Character)Player.m_localPlayer).IsDead())
			{
				ProfessionUI.Hide();
				((Component)InventoryGui.instance.m_textsDialog).gameObject.SetActive(false);
			}
			else if (Input.GetKeyDown(ConfigurationFile.compendiumHotKey.Value) && IsKeyActionAllowed())
			{
				if (((Component)InventoryGui.instance.m_textsDialog).gameObject.activeSelf)
				{
					ProfessionUI.Hide();
					UIManager.CloseInventoryGui();
				}
				else
				{
					InventoryGui.instance.Show((Container)null, 1);
					WaitForSecondsAsync(0.15f);
				}
			}
			else if (Input.GetKeyDown(ConfigurationFile.professionsHotKey.Value) && IsKeyActionAllowed())
			{
				if (ProfessionUI.IsVisible())
				{
					ProfessionUI.Hide();
					UIManager.CloseInventoryGui();
				}
				else
				{
					InventoryGui.instance.Show((Container)null, 1);
					WaitForSecondsAsync(0.15f, openProfessions: true);
				}
			}
		}

		private static bool IsKeyActionAllowed()
		{
			GameObject val = GameObject.Find("_GameMain/LoadingGUI/PixelFix/IngameGui/EasySpawnerMenu(Clone)/SearchInputField");
			InputField val2 = ((val != null) ? val.GetComponent<InputField>() : null);
			return !Chat.instance.HasFocus() && !Console.IsVisible() && Time.timeScale > 0f && !Minimap.IsOpen() && ((Object)(object)val2 == (Object)null || !val2.isFocused);
		}

		private static async Task WaitForSecondsAsync(float seconds, bool openProfessions = false)
		{
			await Task.Delay((int)(Math.Max(0f, seconds) * 1000f));
			InventoryGui.instance.m_textsDialog.Setup(Player.m_localPlayer);
			((Component)InventoryGui.instance.m_textsDialog).gameObject.SetActive(true);
			if (openProfessions)
			{
				ProfessionUI.Show();
			}
		}
	}
	[HarmonyPatch(typeof(Player), "OnSpawned")]
	public static class InstantiateSelectPanel
	{
		private static void Postfix(Player __instance)
		{
			if (ProfessionUI.professionsPanel == null)
			{
				ProfessionUI.professionsPanel = new ProfessionsPanel();
				ProfessionUI.professionsPanel.getPanel().SetActive(false);
			}
			if (ProfessionUI.selectedProfessionPanel == null)
			{
				ProfessionUI.selectedProfessionPanel = new SelectedProfessionPanel();
				ProfessionUI.selectedProfessionPanel.getPanel().SetActive(false);
			}
		}
	}
}
namespace CraftByProfession.UISelection
{
	[HarmonyPatch]
	public class CompendiumPatch
	{
		private static readonly List<ProfessionData> detectedActions = new List<ProfessionData>();

		private static MethodBase TargetMethod()
		{
			return AccessTools.Method(typeof(TextsDialog), "AddActiveEffects", (Type[])null, (Type[])null);
		}

		private static void Postfix(ref TextsDialog __instance)
		{
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Expected O, but got Unknown
			List<TextInfo> list = (List<TextInfo>)GameManager.GetPrivateValue(__instance, "m_texts");
			string playerProfession = PlayerInfoManager.GetPlayerProfession();
			detectedActions.Clear();
			StringBuilder stringBuilder = new StringBuilder(256);
			stringBuilder.Append("<color=orange>" + ConfigurationFile.profession.Value + "</color>\n");
			stringBuilder.Append(playerProfession);
			AddPlayerPieces(stringBuilder, playerProfession);
			AddPlayerRecipes(stringBuilder, playerProfession);
			AddPlayerActions(stringBuilder, playerProfession);
			list.Insert(0, new TextInfo("Valheim Profession Roleplay", stringBuilder.ToString()));
			ProfessionUI.createProfessionUI();
		}

		private static void AddPlayerPieces(StringBuilder stringBuilder, string playerProfession)
		{
			List<ProfessionData> data = JsonManager.LoadJsonFromEmbeddedResource<List<ProfessionData>>("professions.pieces_" + playerProfession + ".json");
			SortedSet<string> items = GetItems(data);
			if (items.Count <= 0)
			{
				return;
			}
			string text = Localization.instance.Localize("$item_parts");
			string text2 = text.ToCharArray()[0].ToString().ToUpper() + text.Substring(1);
			stringBuilder.Append("\n<color=orange>" + text2 + "</color>\n");
			foreach (string item in items)
			{
				stringBuilder.Append(item);
			}
		}

		private static void AddPlayerRecipes(StringBuilder stringBuilder, string playerProfession)
		{
			List<ProfessionData> data = JsonManager.LoadJsonFromEmbeddedResource<List<ProfessionData>>("professions.recipes_" + playerProfession + ".json");
			SortedSet<string> items = GetItems(data);
			if (items.Count <= 0)
			{
				return;
			}
			stringBuilder.Append("\n<color=orange>$inventory_recipes</color>\n");
			foreach (string item in items)
			{
				stringBuilder.Append(item);
			}
		}

		private static void AddPlayerActions(StringBuilder stringBuilder, string playerProfession)
		{
			Logger.Log("**Adding actions");
			SortedSet<string> sortedSet = new SortedSet<string>();
			foreach (ProfessionData detectedAction in detectedActions)
			{
				if (detectedAction.professionName == playerProfession)
				{
					sortedSet.Add(Localization.instance.Localize(detectedAction.ItemName));
				}
			}
			if (sortedSet.Count <= 0)
			{
				return;
			}
			stringBuilder.Append("\n<color=orange>" + ConfigurationFile.actions.Value + "</color>");
			foreach (string item in sortedSet)
			{
				stringBuilder.Append("\n" + item);
			}
		}

		private static SortedSet<string> GetItems(List<ProfessionData> data)
		{
			//IL_0123: Unknown result type (might be due to invalid IL or missing references)
			//IL_0146: Unknown result type (might be due to invalid IL or missing references)
			//IL_014b: Unknown result type (might be due to invalid IL or missing references)
			SortedSet<string> sortedSet = new SortedSet<string>();
			HashSet<string> hashSet = (HashSet<string>)GameManager.GetPrivateValue(Player.m_localPlayer, "m_knownRecipes");
			foreach (ProfessionData item in data ?? new List<ProfessionData>())
			{
				if (IsATool(item) || item.ObjectName == "Recipe_Bronze5")
				{
					continue;
				}
				string text = item.ObjectName.Replace("Recipe_", "").ToLower();
				if (ConfigurationFile.showAllInCompendium.Value || hashSet.Contains("$" + text) || hashSet.Contains("$piece_" + text) || hashSet.Contains("$item_" + text) || hashSet.Contains(item.ItemName))
				{
					string text2 = ((item.ItemName == null) ? item.ObjectName : ((Localization.instance.Localize(item.ItemName) != null) ? Localization.instance.Localize(item.ItemName) : item.ItemName));
					if ((int)item.SkillType != 0 && item.MinSkillLevel > 0)
					{
						SkillType skillType = item.SkillType;
						sortedSet.Add($"{text2} (skill = $skill_{((object)(SkillType)(ref skillType)).ToString().ToLower()}, $msg_level >= {item.MinSkillLevel})\n");
					}
					else
					{
						sortedSet.Add(text2 + "\n");
					}
				}
				else
				{
					Logger.Log(item.ObjectName + " not learnt yet.");
				}
			}
			return sortedSet;
		}

		private static bool IsATool(ProfessionData jsonData)
		{
			if (jsonData.ObjectName == LoaderPiecePatch.TOOL_CULTIVATE)
			{
				detectedActions.Add(LoaderPiecePatch.cultivate);
				return true;
			}
			if (jsonData.ObjectName == LoaderPiecePatch.TOOL_REPLANT)
			{
				detectedActions.Add(LoaderPiecePatch.replant);
				return true;
			}
			if (jsonData.ObjectName == LoaderPiecePatch.TOOL_PAVE)
			{
				detectedActions.Add(LoaderPiecePatch.paved);
				return true;
			}
			if (jsonData.ObjectName == LoaderPiecePatch.TOOL_RAISE)
			{
				detectedActions.Add(LoaderPiecePatch.raise);
				return true;
			}
			return false;
		}
	}
	public class SelectedProfessionPanel
	{
		private GameObject currentProfessionPanel;

		private TextMeshProUGUI selectedProfessionText;

		private Image imageProfessionLogo;

		public SelectedProfessionPanel()
		{
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Expected O, but got Unknown
			//IL_007a: 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_00d0: 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_0132: Unknown result type (might be due to invalid IL or missing references)
			//IL_0159: Unknown result type (might be due to invalid IL or missing references)
			//IL_0163: Expected O, but got Unknown
			//IL_018a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0199: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_0224: Unknown result type (might be due to invalid IL or missing references)
			//IL_0233: Unknown result type (might be due to invalid IL or missing references)
			currentProfessionPanel = new GameObject("CurrentProfessionPanel", new Type[1] { typeof(RectTransform) });
			currentProfessionPanel.SetActive(false);
			currentProfessionPanel.transform.SetParent(((Component)((Component)InventoryGui.instance).transform.Find("root/Texts/Texts_frame")).transform, false);
			RectTransform component = currentProfessionPanel.GetComponent<RectTransform>();
			component.sizeDelta = new Vector2(512f, 512f);
			component.anchoredPosition = Vector2.zero;
			Image val = currentProfessionPanel.AddComponent<Image>();
			val.sprite = ResourcesManager.getSprite("woodpanel_512x512");
			val.type = (Type)1;
			TextMeshProUGUI val2 = UIManager.createUIText("Title", currentProfessionPanel.transform, new Vector2(0f, 220f), "Profesión", 36, "Valheim-Norsebold");
			((Graphic)val2).color = Color.yellow;
			((TMP_Text)val2).alignment = (TextAlignmentOptions)514;
			Button val3 = UIManager.createUIButton("ProfessionOptionsCloseButton", ((Component)InventoryGui.instance.m_skillsDialog).transform.Find("SkillsFrame/Closebutton"), currentProfessionPanel.transform, new Vector2(0f, 40f), Localization.instance.Localize("$menu_close"));
			((UnityEvent)val3.onClick).AddListener((UnityAction)delegate
			{
				currentProfessionPanel.SetActive(false);
			});
			Sprite sprite = UIManager.LoadSpriteFromEmbedded("icons.hijos-de-freyja.png");
			Image val4 = UIManager.createUIImage("LogoHDFEmbedded", currentProfessionPanel.transform, new Vector2(200f, 200f), new Vector2(0f, 110f), sprite);
			val4.preserveAspect = true;
			string playerProfession = PlayerInfoManager.GetPlayerProfession();
			selectedProfessionText = UIManager.createUIText("SelectedProfessionTextGo", currentProfessionPanel.transform, Vector2.zero, playerProfession, 20);
			((Graphic)selectedProfessionText).color = Color.green;
			((TMP_Text)selectedProfessionText).alignment = (TextAlignmentOptions)514;
			Sprite professionSprite = getProfessionSprite(playerProfession);
			imageProfessionLogo = UIManager.createUIImage("LogoProfession", currentProfessionPanel.transform, new Vector2(150f, 150f), new Vector2(0f, -100f), professionSprite);
			imageProfessionLogo.preserveAspect = true;
		}

		private Sprite getProfessionSprite(string selectedProfession)
		{
			string[] array = ConfigurationFile.professions.Value.Split(new char[1] { ',' });
			string[] array2 = ConfigurationFile.professionsSprites.Value.Split(new char[1] { ',' });
			for (int i = 0; i < array.Length; i++)
			{
				if (array[i] == selectedProfession)
				{
					return ResourcesManager.getSprite(array2[i]);
				}
			}
			Logger.LogError("Sprite for Profession '" + selectedProfession + "' not found");
			return null;
		}

		public GameObject getPanel()
		{
			return currentProfessionPanel;
		}

		public void refreshContent()
		{
			if (PlayerInfoManager.GetPlayerProfession() != null)
			{
				((TMP_Text)selectedProfessionText).text = PlayerInfoManager.GetPlayerProfession();
				imageProfessionLogo.sprite = getProfessionSprite(PlayerInfoManager.GetPlayerProfession());
			}
		}
	}
	public class ProfessionsPanel
	{
		private GameObject customProfessionsPanel;

		private Dictionary<string, GameObject> selectProfessionGoBtns = new Dictionary<string, GameObject>();

		private Dictionary<string, TextMeshProUGUI> professionTexts = new Dictionary<string, TextMeshProUGUI>();

		public ProfessionsPanel()
		{
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Expected O, but got Unknown
			//IL_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_009c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fe: Unknown result type (might be due to invalid IL or missing references)
			//IL_0148: Unknown result type (might be due to invalid IL or missing references)
			//IL_016f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0179: Expected O, but got Unknown
			//IL_0194: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_0254: Unknown result type (might be due to invalid IL or missing references)
			//IL_0276: Unknown result type (might be due to invalid IL or missing references)
			//IL_0280: Expected O, but got Unknown
			customProfessionsPanel = new GameObject("CustomProfessionsPanel", new Type[1] { typeof(RectTransform) });
			customProfessionsPanel.SetActive(false);
			customProfessionsPanel.transform.SetParent(((Component)((Component)InventoryGui.instance).transform.Find("root/Texts/Texts_frame")).transform, false);
			RectTransform component = customProfessionsPanel.GetComponent<RectTransform>();
			component.sizeDelta = new Vector2(512f, 512f);
			component.anchoredPosition = Vector2.zero;
			Image val = customProfessionsPanel.AddComponent<Image>();
			val.sprite = ResourcesManager.getSprite("woodpanel_512x512");
			val.type = (Type)1;
			TextMeshProUGUI val2 = UIManager.createUIText("Title", customProfessionsPanel.transform, new Vector2(0f, 223f), "Profesiones", 36, "Valheim-Norsebold");
			((Graphic)val2).color = Color.yellow;
			((TMP_Text)val2).alignment = (TextAlignmentOptions)514;
			Button val3 = UIManager.createUIButton("ProfessionOptionsCloseButton", ((Component)InventoryGui.instance.m_skillsDialog).transform.Find("SkillsFrame/Closebutton"), customProfessionsPanel.transform, new Vector2(0f, 40f), Localization.instance.Localize("$menu_close"));
			((UnityEvent)val3.onClick).AddListener((UnityAction)delegate
			{
				customProfessionsPanel.SetActive(false);
			});
			TextMeshProUGUI val4 = UIManager.createUIText("AlertText", customProfessionsPanel.transform, new Vector2(0f, 165f), "ADVERTENCIA: Elegir profesión es permanente.\n sólo se podrá cambiar borrando el personaje\ny empezando desde cero.", 24, "Valheim-Norsebold");
			((Graphic)val4).color = Color.white;
			((TMP_Text)val4).autoSizeTextContainer = true;
			((TMP_Text)val4).alignment = (TextAlignmentOptions)514;
			string[] array = ConfigurationFile.professions.Value.Split(new char[1] { ',' });
			int num = 100;
			int num2 = 355;
			string[] array2 = array;
			foreach (string profession in array2)
			{
				Transform copyFrom = ((Component)InventoryGui.instance.m_skillsDialog).transform.Find("SkillsFrame/Closebutton");
				Button val5 = UIManager.createUIButton("ProfessionBtnGo", copyFrom, customProfessionsPanel.transform, new Vector2(0f, (float)num2), profession);
				((UnityEvent)val5.onClick).AddListener((UnityAction)delegate
				{
					((Character)Player.m_localPlayer).Message((MessageType)2, "Ya no puedes cambiar de profesión.\nA partir de ahora serás: " + profession, 0, (Sprite)null);
					((Humanoid)Player.m_localPlayer).AddUniqueKey("HDF_" + profession);
					customProfessionsPanel.SetActive(false);
					UIManager.CloseInventoryGui();
					CraftStationDescriptionsPatch.updated = false;
					ResourcesManager.TriggerLightningEffect();
				});
				((Component)val5).gameObject.SetActive(true);
				selectProfessionGoBtns.Add(profession, ((Component)val5).gameObject);
				num -= 50;
				num2 -= 50;
			}
		}

		public GameObject getPanel()
		{
			return customProfessionsPanel;
		}
	}
	[HarmonyPatch]
	public class ProfessionUI
	{
		[Serializable]
		[CompilerGenerated]
		private sealed class <>c
		{
			public static readonly <>c <>9 = new <>c();

			public static UnityAction <>9__4_0;

			public static UnityAction <>9__4_1;

			internal void <createProfessionUI>b__4_0()
			{
				Logger.Log("ProfessionUIOpenButton clicked.");
				string playerProfession = PlayerInfoManager.GetPlayerProfession();
				if (playerProfession == null)
				{
					professionsPanel.getPanel().SetActive(true);
					selectedProfessionPanel.getPanel().SetActive(false);
				}
				else
				{
					professionsPanel.getPanel().SetActive(false);
					selectedProfessionPanel.refreshContent();
					selectedProfessionPanel.getPanel().SetActive(true);
				}
			}

			internal void <createProfessionUI>b__4_1()
			{
				Logger.Log("ProfessionUIResetButton clicked.");
				string[] array = ConfigurationFile.professions.Value.Split(new char[1] { ',' });
				string[] array2 = array;
				foreach (string text in array2)
				{
					((Humanoid)Player.m_localPlayer).RemoveUniqueKey("HDF_" + text);
				}
				CraftStationDescriptionsPatch.updated = false;
				Hide();
				UIManager.CloseInventoryGui();
				ResourcesManager.TriggerLightningEffect();
			}
		}

		private static Button professionUIOpenButton;

		public static Button professionUIResetButton;

		public static ProfessionsPanel professionsPanel;

		public static SelectedProfessionPanel selectedProfessionPanel;

		public static void createProfessionUI()
		{
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0118: Unknown result type (might be due to invalid IL or missing references)
			//IL_00db: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e6: Expected O, but got Unknown
			//IL_014a: Unknown result type (might be due to invalid IL or missing references)
			//IL_014f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0155: Expected O, but got Unknown
			if (professionsPanel == null)
			{
				professionsPanel = new ProfessionsPanel();
				professionsPanel.getPanel().SetActive(false);
			}
			if (selectedProfessionPanel == null)
			{
				selectedProfessionPanel = new SelectedProfessionPanel();
				selectedProfessionPanel.getPanel().SetActive(false);
			}
			Transform val = ((Component)InventoryGui.instance).transform.Find("root/Texts/Texts_frame/Closebutton");
			((Component)val).GetComponent<RectTransform>().anchoredPosition = new Vector2(170f, 55f);
			if ((Object)(object)professionUIOpenButton == (Object)null)
			{
				professionUIOpenButton = UIManager.createUIButton("ProfessionUIOpenButton", val, val.parent, new Vector2(-95f, 55f), "Profesiones");
				ButtonClickedEvent onClick = professionUIOpenButton.onClick;
				object obj = <>c.<>9__4_0;
				if (obj == null)
				{
					UnityAction val2 = delegate
					{
						Logger.Log("ProfessionUIOpenButton clicked.");
						string playerProfession = PlayerInfoManager.GetPlayerProfession();
						if (playerProfession == null)
						{
							professionsPanel.getPanel().SetActive(true);
							selectedProfessionPanel.getPanel().SetActive(false);
						}
						else
						{
							professionsPanel.getPanel().SetActive(false);
							selectedProfessionPanel.refreshContent();
							selectedProfessionPanel.getPanel().SetActive(true);
						}
					};
					<>c.<>9__4_0 = val2;
					obj = (object)val2;
				}
				((UnityEvent)onClick).AddListener((UnityAction)obj);
			}
			if (!((Object)(object)professionUIResetButton == (Object)null))
			{
				return;
			}
			professionUIResetButton = UIManager.createUIButton("ProfessionUIResetButton", val, val.parent, new Vector2(435f, 55f), "Reset profesión");
			ButtonClickedEvent onClick2 = professionUIResetButton.onClick;
			object obj2 = <>c.<>9__4_1;
			if (obj2 == null)
			{
				UnityAction val3 = delegate
				{
					Logger.Log("ProfessionUIResetButton clicked.");
					string[] array = ConfigurationFile.professions.Value.Split(new char[1] { ',' });
					string[] array2 = array;
					foreach (string text in array2)
					{
						((Humanoid)Player.m_localPlayer).RemoveUniqueKey("HDF_" + text);
					}
					CraftStationDescriptionsPatch.updated = false;
					Hide();
					UIManager.CloseInventoryGui();
					ResourcesManager.TriggerLightningEffect();
				};
				<>c.<>9__4_1 = val3;
				obj2 = (object)val3;
			}
			((UnityEvent)onClick2).AddListener((UnityAction)obj2);
			Logger.Log("ProfessionUIResetButton bypassAdmins: " + ConfigurationFile.bypassAdmins.Value);
			Logger.Log("ProfessionUIResetButton allowProfessionChange: " + ConfigurationFile.allowProfessionChange.Value);
			((Component)professionUIResetButton).gameObject.SetActive(ConfigurationFile.bypassAdmins.Value || ConfigurationFile.allowProfessionChange.Value);
		}

		public static bool IsVisible()
		{
			return professionsPanel.getPanel().activeSelf || selectedProfessionPanel.getPanel().activeSelf;
		}

		public static void Hide()
		{
			ProfessionsPanel obj = professionsPanel;
			if (obj != null)
			{
				GameObject panel = obj.getPanel();
				if (panel != null)
				{
					panel.SetActive(false);
				}
			}
			SelectedProfessionPanel obj2 = selectedProfessionPanel;
			if (obj2 != null)
			{
				GameObject panel2 = obj2.getPanel();
				if (panel2 != null)
				{
					panel2.SetActive(false);
				}
			}
		}

		public static void Show()
		{
			string playerProfession = PlayerInfoManager.GetPlayerProfession();
			if (playerProfession == null)
			{
				ProfessionsPanel obj = professionsPanel;
				if (obj != null)
				{
					GameObject panel = obj.getPanel();
					if (panel != null)
					{
						panel.SetActive(true);
					}
				}
				SelectedProfessionPanel obj2 = selectedProfessionPanel;
				if (obj2 != null)
				{
					GameObject panel2 = obj2.getPanel();
					if (panel2 != null)
					{
						panel2.SetActive(false);
					}
				}
				return;
			}
			ProfessionsPanel obj3 = professionsPanel;
			if (obj3 != null)
			{
				GameObject panel3 = obj3.getPanel();
				if (panel3 != null)
				{
					panel3.SetActive(false);
				}
			}
			selectedProfessionPanel?.refreshContent();
			SelectedProfessionPanel obj4 = selectedProfessionPanel;
			if (obj4 != null)
			{
				GameObject panel4 = obj4.getPanel();
				if (panel4 != null)
				{
					panel4.SetActive(true);
				}
			}
		}
	}
}
namespace CraftByProfession.Loaders
{
	[HarmonyPatch(typeof(ZNetScene), "Awake")]
	public static class LoaderPiecePatch
	{
		public static readonly string TOOL_CULTIVATE = "cultivate_v2";

		public static readonly string TOOL_REPLANT = "replant_v2";

		public static readonly string TOOL_PAVE = "paved_road_v2";

		public static readonly string TOOL_RAISE = "raise_v2";

		public static ProfessionData cultivate;

		public static ProfessionData replant;

		public static ProfessionData paved;

		public static ProfessionData raise;

		private static void init()
		{
			cultivate = null;
			replant = null;
			paved = null;
			raise = null;
		}

		public static void Postfix(ZNetScene __instance)
		{
			init();
			string[] array = ConfigurationFile.professions.Value.Split(new char[1] { ',' });
			string[] array2 = array;
			foreach (string professionName in array2)
			{
				LoadFilePieces(professionName);
			}
		}

		private static void LoadFilePieces(string professionName)
		{
			//IL_00f2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f7: Unknown result type (might be due to invalid IL or missing references)
			List<ProfessionData> list = JsonManager.LoadJsonFromEmbeddedResource<List<ProfessionData>>("professions.pieces_" + professionName + ".json");
			Logger.Log("** Loading Pieces for " + professionName + "...");
			foreach (ProfessionData dataItem in list ?? new List<ProfessionData>())
			{
				if (!foundATool(dataItem, professionName))
				{
					GameObject val = ZNetScene.instance.m_prefabs.Find((GameObject i) => ((Object)i).name == dataItem.ObjectName);
					if ((Object)(object)val != (Object)null)
					{
						ProfessionDataComponent professionDataComponent = (((Object)(object)val.GetComponent<ProfessionDataComponent>() != (Object)null) ? val.GetComponent<ProfessionDataComponent>() : val.AddComponent<ProfessionDataComponent>());
						professionDataComponent.objectName = dataItem.ObjectName;
						professionDataComponent.itemName = dataItem.ItemName;
						professionDataComponent.professionName = professionName;
						professionDataComponent.skillType = dataItem.SkillType;
						professionDataComponent.minSkillLevel = dataItem.MinSkillLevel;
						Logger.Log("Prefab profession added to " + professionDataComponent.objectName + " for profession " + professionDataComponent.professionName + ".");
					}
					else
					{
						Logger.LogWarning("Prefab not found with name " + dataItem.ObjectName + ". Skipping.");
					}
				}
			}
		}

		private static bool foundATool(ProfessionData dataItem, string professionName)
		{
			if (dataItem.ObjectName == TOOL_CULTIVATE)
			{
				Logger.Log(TOOL_CULTIVATE + " found");
				dataItem.professionName = professionName;
				cultivate = dataItem;
				return true;
			}
			if (dataItem.ObjectName == TOOL_REPLANT)
			{
				Logger.Log(TOOL_REPLANT + " found");
				dataItem.professionName = professionName;
				replant = dataItem;
				return true;
			}
			if (dataItem.ObjectName == TOOL_PAVE)
			{
				Logger.Log(TOOL_PAVE + " found");
				dataItem.professionName = professionName;
				paved = dataItem;
				return true;
			}
			if (dataItem.ObjectName == TOOL_RAISE)
			{
				Logger.Log(TOOL_RAISE + " found");
				dataItem.professionName = professionName;
				raise = dataItem;
				return true;
			}
			return false;
		}
	}
	[HarmonyPatch(typeof(ZNetScene), "Awake")]
	public static class LoaderRecipePatch
	{
		public static void Postfix(ZNetScene __instance)
		{
			RecipeManager.recipesProfessionData.Clear();
			string[] array = ConfigurationFile.professions.Value.Split(new char[1] { ',' });
			string[] array2 = array;
			foreach (string professionName in array2)
			{
				loadRecipes(professionName);
			}
		}

		private static void loadRecipes(string professionName)
		{
			List<ProfessionData> list = JsonManager.LoadJsonFromEmbeddedResource<List<ProfessionData>>("professions.recipes_" + professionName + ".json");
			foreach (ProfessionData item in list ?? new List<ProfessionData>())
			{
				if (!RecipeManager.recipesProfessionData.ContainsKey(item.ObjectName))
				{
					item.professionName = professionName;
					RecipeManager.recipesProfessionData.Add(item.ObjectName, item);
					Logger.Log("Profession Data from json added: " + item.ObjectName + " for profession " + item.professionName);
				}
				else
				{
					Logger.LogWarning("Profession Data repeatedKey " + item.ObjectName);
				}
			}
		}
	}
}
namespace CraftByProfession.Patches
{
	[HarmonyPatch(typeof(Player), "Interact")]
	public static class PlayerInteractPatch
	{
		private static bool Prefix(Player __instance, GameObject go, bool hold, bool alt)
		{
			Logger.Log("**PlayerInteractPatch Interact PREFIX");
			if (ConfigurationFile.bypassAdmins.Value)
			{
				bool flag = PlayerInfoManager.IsPlayerLocalAdmin();
				Logger.Log($"PlayerInteractPatch isAdmin: {flag}");
				if (flag)
				{
					return true;
				}
				string steamID = PlayerInfoManager.GetSteamID(__instance);
				Logger.Log("steamId = " + steamID);
				if (steamID == null)
				{
					return true;
				}
				flag = PlayerInfoManager.IsAdmin(steamID);
				Logger.Log("PlayerInteractPatch " + steamID + " isAdmin: " + flag);
				if (flag)
				{
					return true;
				}
			}
			Transform parent = go.transform.parent;
			string text = ((parent != null) ? ((Object)((Component)parent).gameObject).name : null);
			if (text == null)
			{
				return true;
			}
			Logger.Log("gameObject name: " + ((Object)go).name);
			Logger.Log("gameObject parent name: " + text);
			if (text.ToLower().StartsWith("piece_beehive"))
			{
				return checkPiece(__instance, ConfigurationFile.beehiveProfession.Value, ConfigurationFile.beehiveUsageForbidden.Value);
			}
			if (text.ToLower().StartsWith("smelter"))
			{
				return checkPiece(__instance, ConfigurationFile.smelterProfession.Value, ConfigurationFile.smelterUsageForbidden.Value);
			}
			if (text.ToLower().StartsWith("blastfurnace"))
			{
				return checkPiece(__instance, ConfigurationFile.blastfurnaceProfession.Value, ConfigurationFile.blastfurnaceUsageForbidden.Value);
			}
			if (text.ToLower().StartsWith("eitrrefinery"))
			{
				return checkPiece(__instance, ConfigurationFile.eitrrefineryProfession.Value, ConfigurationFile.eitrrefineryUsageForbidden.Value);
			}
			Transform parent2 = go.transform.parent;
			if ((Object)(object)((parent2 != null) ? parent2.parent : null) != (Object)null)
			{
				Transform parent3 = go.transform.parent;
				object obj;
				if (parent3 == null)
				{
					obj = null;
				}
				else
				{
					Transform parent4 = parent3.parent;
					obj = ((parent4 != null) ? ((Component)parent4).gameObject : null);
				}
				if ((Object)obj != (Object)null)
				{
					Logger.Log("Checking last option");
					Transform parent5 = go.transform.parent;
					object obj2;
					if (parent5 == null)
					{
						obj2 = null;
					}
					else
					{
						Transform parent6 = parent5.parent;
						obj2 = ((parent6 != null) ? ((Object)((Component)parent6).gameObject).name : null);
					}
					string text2 = (string)obj2;
					Logger.Log("gameObject parent parent name: " + text2);
					if (text2.ToLower().StartsWith("fermenter"))
					{
						return checkPiece(__instance, ConfigurationFile.fermenterProfession.Value, ConfigurationFile.fermenterUsageForbidden.Value);
					}
				}
			}
			return true;
		}

		private static bool checkPiece(Player __instance, string professionName, string unavailableMessage)
		{
			if (professionName != PlayerInfoManager.GetPlayerProfession())
			{
				Logger.Log("Interact bloqueado");
				((Character)__instance).Message((MessageType)2, unavailableMessage, 0, (Sprite)null);
				return false;
			}
			Logger.Log("Interact permitido");
			return true;
		}
	}
	[HarmonyPatch(typeof(InventoryGui), "Show")]
	public static class CraftStationDescriptionsPatch
	{
		public static bool updated;

		public static bool Prefix(Container container, int activeGroup)
		{
			//IL_0105: Unknown result type (might be due to invalid IL or missing references)
			//IL_0124: Unknown result type (might be due to invalid IL or missing references)
			//IL_0129: Unknown result type (might be due to invalid IL or missing references)
			if (updated)
			{
				return true;
			}
			foreach (Recipe recipe in ObjectDB.instance.m_recipes)
			{
				if (RecipeManager.recipesProfessionData.ContainsKey(((Object)recipe).name))
				{
					string playerProfession = PlayerInfoManager.GetPlayerProfession();
					ProfessionData valueSafe = GeneralExtensions.GetValueSafe<string, ProfessionData>(RecipeManager.recipesProfessionData, ((Object)recipe).name);
					string professionName = valueSafe.professionName;
					string text = ((playerProfession == professionName) ? "green" : "red");
					string text2 = recipe.m_item.m_itemData.m_shared.m_description.Split(new char[1] { '\n' })[0];
					SharedData shared = recipe.m_item.m_itemData.m_shared;
					string[] obj = new string[9]
					{
						text2,
						"\n",
						ConfigurationFile.profession.Value,
						": <color=",
						text,
						">",
						professionName,
						"</color>",
						null
					};
					object obj2;
					if ((int)valueSafe.SkillType == 0 || valueSafe.MinSkillLevel <= 0)
					{
						obj2 = "";
					}
					else
					{
						SkillType skillType = valueSafe.SkillType;
						obj2 = $"\n(Skill = $skill_{((object)(SkillType)(ref skillType)).ToString().ToLower()}, $msg_level = {valueSafe.MinSkillLevel})";
					}
					obj[8] = (string)obj2;
					shared.m_description = string.Concat(obj);
				}
			}
			updated = true;
			return true;
		}
	}
	[HarmonyPatch(typeof(UITooltip), "UpdateTextElements")]
	public static class UITooltipPatch
	{
		public static void Postfix(UITooltip __instance)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Expected O, but got Unknown
			//IL_00f8: 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_011c: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = (GameObject)GameManager.GetPrivateValue(__instance, "m_tooltip", BindingFlags.Static | BindingFlags.NonPublic);
			if (!((Object)(object)val != (Object)null))
			{
				return;
			}
			Transform val2 = Utils.FindChild(val.transform, "Text", (IterativeSearchType)0);
			if (!((Object)(object)val2 != (Object)null))
			{
				return;
			}
			ProfessionData professionData = RecipeManager.FindRecipeByItemName(__instance.m_topic);
			if (professionData?.ObjectName == null)
			{
				return;
			}
			string playerProfession = PlayerInfoManager.GetPlayerProfession();
			string professionName = professionData.professionName;
			string text = ((playerProfession == professionName) ? "green" : "red");
			if (!__instance.m_text.Contains(ConfigurationFile.profession.Value))
			{
				string[] obj = new string[8]
				{
					"\n",
					ConfigurationFile.profession.Value,
					": <color=",
					text,
					">",
					professionName,
					"</color>",
					null
				};
				object obj2;
				if ((int)professionData.SkillType == 0 || professionData.MinSkillLevel <= 0)
				{
					obj2 = "";
				}
				else
				{
					SkillType skillType = professionData.SkillType;
					obj2 = $"\n(Skill = $skill_{((object)(SkillType)(ref skillType)).ToString().ToLower()}, $msg_level = {professionData.MinSkillLevel})";
				}
				obj[7] = (string)obj2;
				string text2 = string.Concat(obj);
				__instance.m_text = __instance.m_text.Replace("_description", "_description " + text2);
			}
			((Component)val2).GetComponent<TMP_Text>().text = Localization.instance.Localize(__instance.m_text);
		}
	}
	[HarmonyPatch(typeof(Player), "TryPlacePiece")]
	public static class PlacePiecePatch
	{
		private static bool Prefix(Player __instance, Piece piece)
		{
			//IL_0123: Unknown result type (might be due to invalid IL or missing references)
			//IL_0128: Unknown result type (might be due to invalid IL or missing references)
			Logger.Log("**PlacePiecePatch TryPlacePiece PREFIX");
			string steamID = PlayerInfoManager.GetSteamID(__instance);
			if (steamID == null)
			{
				return true;
			}
			Logger.Log("steamId = " + steamID);
			if (ConfigurationFile.bypassAdmins.Value)
			{
				bool flag = PlayerInfoManager.IsPlayerLocalAdmin();
				Logger.Log($"isAdmin: {flag}");
				if (flag)
				{
					return true;
				}
				flag = PlayerInfoManager.IsAdmin(steamID);
				Logger.Log($"{steamID} isAdmin: {flag}");
				if (flag)
				{
					return true;
				}
			}
			if (!IsAllowed(steamID, piece))
			{
				ProfessionData professionData = PlayerInfoManager.GetProfessionData(piece);
				string errorMessage = GetErrorMessage(ConfigurationFile.unavailablePieceMessage.Value, professionData.professionName);
				((Character)__instance).Message((MessageType)2, errorMessage, 0, (Sprite)null);
				return false;
			}
			if (!IsAllowedByLevel(__instance, piece))
			{
				ProfessionData professionData2 = PlayerInfoManager.GetProfessionData(piece);
				string value = ConfigurationFile.unavailablePieceLevelMessage.Value;
				string replace = professionData2.MinSkillLevel.ToString();
				Localization instance = Localization.instance;
				SkillType skillType = professionData2.SkillType;
				string errorMessage2 = GetErrorMessage(value, replace, instance.Localize("$skill_" + ((object)(SkillType)(ref skillType)).ToString().ToLower()));
				((Character)__instance).Message((MessageType)2, errorMessage2, 0, (Sprite)null);
				return false;
			}
			Logger.Log("**Player IS PIECE ALLOWED");
			return true;
		}

		public static bool IsAllowed(string steamId, Piece piece)
		{
			Logger.Log("Piece.m_name: " + piece.m_name + ", Piece.name: " + ((Object)piece).name);
			ProfessionData professionData = PlayerInfoManager.GetProfessionData(piece);
			Logger.Log($"Condition: {professionData == null}");
			Logger.Log("pieceProfessionData = " + professionData);
			if (professionData?.ObjectName == null)
			{
				return true;
			}
			string playerProfession = PlayerInfoManager.GetPlayerProfession();
			bool flag = playerProfession != null;
			Logger.Log(steamId + " hasAProfession: " + flag);
			if (!flag)
			{
				return false;
			}
			bool flag2 = professionData.professionName.ToLower().Contains(playerProfession.ToLower());
			Logger.Log($"professionMatching: {professionData.professionName.ToLower()} vs {playerProfession.ToLower()} = {flag2}");
			return flag2;
		}

		public static bool IsAllowedByLevel(Player player, Piece piece)
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Invalid comparison between Unknown and I4
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			ProfessionData professionData = PlayerInfoManager.GetProfessionData(piece);
			if (professionData != null && professionData.MinSkillLevel > 0 && (int)professionData.SkillType > 0)
			{
				List<Skill> skillList = ((Character)player).GetSkills().GetSkillList();
				for (int i = 0; i < skillList.Count; i++)
				{
					Skill val = skillList[i];
					if (val.m_info.m_skill == professionData.SkillType)
					{
						int num = (int)val.m_level;
						bool flag = num >= professionData.MinSkillLevel;
						Logger.Log($"currentLevel {num} necessaryLevel {professionData.MinSkillLevel} professionMatching: {flag}");
						return flag;
					}
				}
			}
			return true;
		}

		private static string GetErrorMessage(string message, string replace0 = null, string replace1 = null)
		{
			if (replace0 != null)
			{
				message = message.Replace("{0}", replace0);
			}
			if (replace1 != null)
			{
				message = message.Replace("{1}", replace1);
			}
			return message;
		}
	}
	[HarmonyPatch(typeof(Player), "CheckCanRemovePiece")]
	public static class CheckCanRemovePiecePatch
	{
		private static bool Prefix(Player __instance, Piece piece)
		{
			//IL_0119: Unknown result type (might be due to invalid IL or missing references)
			//IL_011e: Unknown result type (might be due to invalid IL or missing references)
			Logger.Log("**CheckCanRemovePiecePatch CheckCanRemovePiece PREFIX");
			string steamID = PlayerInfoManager.GetSteamID(__instance);
			if (steamID == null)
			{
				return true;
			}
			Logger.Log("steamId = " + steamID);
			if (ConfigurationFile.bypassAdmins.Value)
			{
				bool flag = PlayerInfoManager.IsPlayerLocalAdmin();
				Logger.Log($"CheckCanRemovePiece.Prefix IsPlayerLocalAdmin: {flag}");
				if (flag)
				{
					return true;
				}
				Logger.Log("CheckCanRemovePiece.Prefix " + steamID + " IsAdmin: " + PlayerInfoManager.IsAdmin(steamID));
			}
			if (!PlacePiecePatch.IsAllowed(steamID, piece))
			{
				ProfessionData professionData = PlayerInfoManager.GetProfessionData(piece);
				string errorMessage = GetErrorMessage(ConfigurationFile.unremovablePieceMessage.Value, professionData.professionName);
				((Character)__instance).Message((MessageType)2, errorMessage, 0, (Sprite)null);
				return false;
			}
			if (!PlacePiecePatch.IsAllowedByLevel(__instance, piece))
			{
				ProfessionData professionData2 = PlayerInfoManager.GetProfessionData(piece);
				string value = ConfigurationFile.unremovablePieceLevelMessage.Value;
				string replace = professionData2.MinSkillLevel.ToString();
				Localization instance = Localization.instance;
				SkillType skillType = professionData2.SkillType;
				string errorMessage2 = GetErrorMessage(value, replace, instance.Localize("$skill_" + ((object)(SkillType)(ref skillType)).ToString().ToLower()));
				((Character)__instance).Message((MessageType)2, errorMessage2, 0, (Sprite)null);
				return false;
			}
			Logger.Log("**Player IS ALLOWED TO REMOVE PIECE");
			return true;
		}

		private static string GetErrorMessage(string message, string replace0 = null, string replace1 = null)
		{
			if (replace0 != null)
			{
				message = message.Replace("{0}", replace0);
			}
			if (replace1 != null)
			{
				message = message.Replace("{1}", replace1);
			}
			return message;
		}
	}
	[HarmonyPatch(typeof(InventoryGui), "OnCraftPressed")]
	public static class RecipeCraftPatch
	{
		private static bool Prefix(InventoryGui __instance)
		{
			//IL_0127: Unknown result type (might be due to invalid IL or missing references)
			//IL_012c: Unknown result type (might be due to invalid IL or missing references)
			Logger.Log("**RecipeCraftPatch OnCraftPressed PREFIX");
			Recipe selectedRecipe = GetSelectedRecipe(__instance);
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)selectedRecipe == (Object)null || (Object)(object)localPlayer == (Object)null)
			{
				Logger.LogError("Error detecting recipe or player. Continue as vanilla.");
				return true;
			}
			ProfessionData professionData = PlayerInfoManager.GetProfessionData(selectedRecipe);
			string text = professionData?.professionName;
			Logger.Log("professionData.professionName = " + text);
			if (text == null)
			{
				Logger.Log("No profession assigned to recipe. Skipping.");
				return true;
			}
			Logger.Log("ProfessionData for recipe " + ((Object)selectedRecipe).name + " with professionName: " + text);
			Logger.Log("ProfessionData: " + professionData.ToString());
			if (!PlayerMeetsProfessionRequirement(professionData))
			{
				string errorMessage = GetErrorMessage(ConfigurationFile.unavailableRecipeMessage.Value, text);
				((Character)localPlayer).Message((MessageType)2, errorMessage, 0, (Sprite)null);
				return false;
			}
			if (!PlayerMeetsProfessionLevelRequirement(localPlayer, professionData))
			{
				string value = ConfigurationFile.unavailableRecipeLevelMessage.Value;
				string replace = professionData.MinSkillLevel.ToString();
				Localization instance = Localization.instance;
				SkillType skillType = professionData.SkillType;
				string errorMessage2 = GetErrorMessage(value, replace, instance.Localize("$skill_" + ((object)(SkillType)(ref skillType)).ToString().ToLower()));
				((Character)localPlayer).Message((MessageType)2, errorMessage2, 0, (Sprite)null);
				return false;
			}
			Logger.Log("**Player IS RECIPE ALLOWED");
			return true;
		}

		private static string GetErrorMessage(string message, string replace0 = null, string replace1 = null)
		{
			if (replace0 != null)
			{
				message = message.Replace("{0}", replace0);
			}
			if (replace1 != null)
			{
				message = message.Replace("{1}", replace1);
			}
			return message;
		}

		private static Recipe GetSelectedRecipe(InventoryGui instance)
		{
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Expected O, but got Unknown
			object privateValue = GameManager.GetPrivateValue(instance, "m_selectedRecipe");
			FieldInfo[] fields = privateValue.GetType().GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			FieldInfo fieldInfo = null;
			FieldInfo[] array = fields;
			foreach (FieldInfo fieldInfo2 in array)
			{
				if (fieldInfo2.Name.Contains("Recipe"))
				{
					Logger.Log("Recipe attribute found");
					fieldInfo = fieldInfo2;
					break;
				}
			}
			Recipe val = (Recipe)(fieldInfo?.GetValue(privateValue));
			Logger.Log("recipe: " + (object)val);
			return val;
		}

		private static bool PlayerMeetsProfessionRequirement(ProfessionData professionData)
		{
			string playerProfession = PlayerInfoManager.GetPlayerProfession();
			Logger.Log("PlayerMeetsProfessionRequirement: " + playerProfession + " vs " + professionData.professionName);
			return playerProfession == professionData.professionName;
		}

		private static bool PlayerMeetsProfessionLevelRequirement(Player player, ProfessionData professionData)
		{
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Invalid comparison between Unknown and I4
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			if (!ConfigurationFile.bypassAdmins.Value && professionData != null && professionData.ObjectName != null && professionData.MinSkillLevel > 0 && (int)professionData.SkillType > 0)
			{
				List<Skill> skillList = ((Character)player).GetSkills().GetSkillList();
				for (int i = 0; i < skillList.Count; i++)
				{
					Skill val = skillList[i];
					if (val.m_info.m_skill == professionData.SkillType)
					{
						int num = (int)val.m_level;
						bool flag = num >= professionData.MinSkillLevel;
						Logger.Log($"PlayerMeetsProfessionLevelRequirement: currentLevel {num} necessaryLevel {professionData.MinSkillLevel}. meets: {flag}");
						return flag;
					}
				}
			}
			return true;
		}
	}
}
namespace CraftByProfession.Managers
{
	public class GameManager
	{
		public static object GetPrivateValue(object obj, string name, BindingFlags bindingAttr = BindingFlags.Instance | BindingFlags.NonPublic)
		{
			return obj.GetType().GetField(name, bindingAttr).GetValue(obj);
		}
	}
	public class JsonManager
	{
		public static T LoadJsonFromEmbeddedResource<T>(string resourceName)
		{
			Assembly executingAssembly = Assembly.GetExecutingAssembly();
			using Stream stream = executingAssembly.GetManifestResourceStream("CraftByProfession." + resourceName);
			if (stream == null)
			{
				Logger.LogWarning("Recurso incrustado '" + resourceName + "' no encontrado.");
				return default(T);
			}
			using StreamReader streamReader = new StreamReader(stream);
			string json = streamReader.ReadToEnd();
			return (T)ParseProfessionDataList(json);
		}

		public static object ParseProfessionDataList(string json)
		{
			//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
			List<ProfessionData> list = new List<ProfessionData>();
			json = json.Trim().TrimStart(new char[1] { '[' }).TrimEnd(new char[1] { ']' });
			string[] array = json.Split(new string[1] { "}," }, StringSplitOptions.RemoveEmptyEntries);
			string[] array2 = array;
			foreach (string text in array2)
			{
				string text2 = text.Trim();
				if (!text2.EndsWith("}"))
				{
					text2 += "}";
				}
				ProfessionData item = new ProfessionData
				{
					ObjectName = ExtractJsonValue(text2, "objectName"),
					ItemName = ExtractJsonValue(text2, "itemName"),
					SkillType = convertSkillType(ExtractJsonValue(text2, "skillType")),
					MinSkillLevel = int.Parse(ExtractJsonValue(text2, "minSkillLevel") ?? "0")
				};
				list.Add(item);
			}
			return list;
		}

		private static SkillType convertSkillType(string jsonValue)
		{
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			if (jsonValue?.ToLower() == "swords")
			{
				return (SkillType)1;
			}
			return (SkillType)0;
		}

		private static string ExtractJsonValue(string json, string key)
		{
			int num = json.IndexOf("\"" + key + "\"", StringComparison.OrdinalIgnoreCase);
			if (num == -1)
			{
				Logger.LogWarning("Key '" + key + "' not found in JSON: " + json);
				return null;
			}
			int num2 = json.IndexOf(":", num, StringComparison.Ordinal) + 1;
			int num3;
			if (json[num2] == '"')
			{
				num2++;
				num3 = json.IndexOf("\"", num2, StringComparison.Ordinal);
			}
			else
			{
				num3 = json.IndexOfAny(new char[2] { ',', '}' }, num2);
			}
			return json.Substring(num2, num3 - num2).Trim().Replace("\"", "");
		}
	}
	public class ResourcesManager
	{
		private static Dictionary<string, Sprite> cachedSprites = new Dictionary<string, Sprite>();

		private static Dictionary<string, TMP_FontAsset> cachedFonts = new Dictionary<string, TMP_FontAsset>();

		public static Sprite getSprite(string name)
		{
			if (!cachedSprites.ContainsKey(name))
			{
				Logger.Log("Finding " + name + " sprite...");
				Sprite[] array = Resources.FindObjectsOfTypeAll<Sprite>();
				foreach (Sprite val in array)
				{
					if (((Object)val).name == name)
					{
						Logger.Log(name + " sprite found.");
						cachedSprites.Add(name, val);
						return val;
					}
				}
				Logger.Log(name + " sprite NOT found.");
				return null;
			}
			return GeneralExtensions.GetValueSafe<string, Sprite>(cachedSprites, name);
		}

		public static TMP_FontAsset getFontAsset(string name)
		{
			if (!cachedFonts.ContainsKey(name))
			{
				Logger.Log("Finding " + name + " font...");
				TMP_FontAsset[] array = Resources.FindObjectsOfTypeAll<TMP_FontAsset>();
				foreach (TMP_FontAsset val in array)
				{
					if (((Object)val).name == name)
					{
						Logger.Log(name + " font found.");
						cachedFonts.Add(name, val);
						return val;
					}
				}
				Logger.Log(name + " font NOT found.");
				return null;
			}
			return GeneralExtensions.GetValueSafe<string, TMP_FontAsset>(cachedFonts, name);
		}

		public static void TriggerLightningEffect()
		{
			SEMan sEMan = ((Character)Player.m_localPlayer).GetSEMan();
			if ((Object)(object)sEMan.GetStatusEffect("Lightning".GetHashCode()) == (Object)null)
			{
				StatusEffect val = sEMan.AddStatusEffect("Lightning".GetHashCode(), false, 0, 0f);
				val.m_ttl = 5f;
			}
		}
	}
	public class UIManager
	{
		private static readonly Color BUTTON_VANILLA_COLOR = new Color(1f, 0.7176f, 0.3603f);

		public static TextMeshProUGUI createUIText(string goName, Transform parent, Vector2 pos, string text, int fontSize, string fontName = null)
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Expected O, but got Unknown
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject(goName, new Type[1] { typeof(TextMeshProUGUI) });
			val.transform.SetParent(parent, false);
			RectTransform component = val.GetComponent<RectTransform>();
			component.anchoredPosition = pos;
			TextMeshProUGUI component2 = val.GetComponent<TextMeshProUGUI>();
			if (fontName != null)
			{
				((TMP_Text)component2).font = ResourcesManager.getFontAsset(fontName);
			}
			((TMP_Text)component2).fontSize = fontSize;
			((TMP_Text)component2).text = text;
			return component2;
		}

		public static Button createUIButton(string goName, Transform copyFrom, Transform parent, Vector2 pos, string text)
		{
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Expected O, but got Unknown
			GameObject val = Object.Instantiate<GameObject>(((Component)copyFrom).gameObject, parent);
			((Object)val).name = goName;
			val.transform.SetParent(parent, false);
			RectTransform component = val.GetComponent<RectTransform>();
			component.anchoredPosition = pos;
			TextMeshProUGUI componentInChildren = val.GetComponentInChildren<TextMeshProUGUI>();
			((TMP_Text)componentInChildren).text = text;
			((TMP_Text)componentInChildren).fontStyle = (FontStyles)0;
			((Graphic)componentInChildren).color = BUTTON_VANILLA_COLOR;
			((TMP_Text)componentInChildren).alignment = (TextAlignmentOptions)514;
			Button component2 = val.GetComponent<Button>();
			component2.onClick = new ButtonClickedEvent();
			return component2;
		}

		public static Image createUIImage(string goName, Transform parent, Vector2 size, Vector2 pos, Sprite sprite, bool shadowBkg = false)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Expected O, but got Unknown
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject(goName);
			val.transform.SetParent(parent, false);
			RectTransform val2 = val.AddComponent<RectTransform>();
			val2.sizeDelta = size;
			val2.anchoredPosition = pos;
			if (shadowBkg)
			{
				Image val3 = createUIImage(goName + "Bkg", val.transform, size, Vector2.zero, null);
				((Graphic)val3).color = new Color(0f, 0f, 0f, 0.5f);
			}
			Image val4 = val.AddComponent<Image>();
			val4.sprite = sprite;
			return val4;
		}

		private static byte[] ReadEmbeddedFileBytes(string name)
		{
			using MemoryStream memoryStream = new MemoryStream();
			Assembly.GetExecutingAssembly().GetManifestResourceStream("CraftByProfession." + name)?.CopyTo(memoryStream);
			return memoryStream.ToArray();
		}

		private static Texture2D loadTexture(string name)
		{
			//IL_0003: Unknown result type (might be due to invalid IL or missing references)
			//IL_0009: Expected O, but got Unknown
			Texture2D val = new Texture2D(0, 0);
			ImageConversion.LoadImage(val, ReadEmbeddedFileBytes("icons." + name));
			return val;
		}

		public static Sprite loadSprite(string name, int width, int height)
		{
			//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)
			return Sprite.Create(loadTexture(name), new Rect(0f, 0f, (float)width, (float)height), Vector2.zero);
		}

		public static Sprite LoadSpriteFromFile(string filePath)
		{
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Expected O, but got Unknown
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			if (!File.Exists(filePath))
			{
				Debug.LogError((object)("File not found at path: " + filePath));
				return null;
			}
			try
			{
				byte[] array = File.ReadAllBytes(filePath);
				Texture2D val = new Texture2D(2, 2);
				if (!ImageConversion.LoadImage(val, array))
				{
					Debug.LogError((object)"Failed to load texture from image data.");
					return null;
				}
				return Sprite.Create(val, new Rect(0f, 0f, (float)((Texture)val).width, (float)((Texture)val).height), new Vector2(0.5f, 0.5f));
			}
			catch (Exception ex)
			{
				Debug.LogError((object)("Failed to load image from file: " + ex.Message));
				return null;
			}
		}

		public static Sprite LoadSpriteFromEmbedded(string embeddedPath)
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Expected O, but got Unknown
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				byte[] array = ReadEmbeddedFileBytes(embeddedPath);
				Texture2D val = new Texture2D(2, 2);
				if (!ImageConversion.LoadImage(val, array))
				{
					Debug.LogError((object)"Failed to load texture from image data.");
					return null;
				}
				return Sprite.Create(val, new Rect(0f, 0f, (float)((Texture)val).width, (float)((Texture)val).height), new Vector2(0.5f, 0.5f));
			}
			catch (Exception ex)
			{
				Debug.LogError((object)("Failed to load image from file: " + ex.Message));
				return null;
			}
		}

		public static void CloseInventoryGui()
		{
			((Component)InventoryGui.instance.m_textsDialog).gameObject.SetActive(false);
			InventoryGui.instance.Hide();
		}
	}
	public class PlayerInfoManager
	{
		public static ProfessionData GetProfessionData(Piece piece)
		{
			if (LoaderPiecePatch.TOOL_CULTIVATE == ((Object)piece).name)
			{
				return LoaderPiecePatch.cultivate;
			}
			if (LoaderPiecePatch.TOOL_REPLANT == ((Object)piece).name)
			{
				return LoaderPiecePatch.replant;
			}
			if (LoaderPiecePatch.TOOL_PAVE == ((Object)piece).name)
			{
				return LoaderPiecePatch.paved;
			}
			if (LoaderPiecePatch.TOOL_RAISE == ((Object)piece).name)
			{
				return LoaderPiecePatch.paved;
			}
			string name = ((Object)piece).name.Replace("$", "").Replace("(Clone)", "");
			Logger.Log("Finding gameObject starting with " + name);
			GameObject val = ZNetScene.instance.m_prefabs.Find((GameObject i) => ((Object)i).name.StartsWith(name));
			ProfessionDataComponent professionDataComponent = ((val != null) ? val.GetComponent<ProfessionDataComponent>() : null);
			if ((Object)(object)professionDataComponent == (Object)null)
			{
				return null;
			}
			return professionDataComponent.getProfessionData();
		}

		public static string GetPlayerProfession()
		{
			string[] array = ConfigurationFile.professions.Value.Split(new char[1] { ',' });
			List<string> list = new List<string>();
			for (int i = 0; i < array.Length; i++)
			{
				list.Add(array[i]);
			}
			foreach (string uniqueKey in Player.m_localPlayer.GetUniqueKeys())
			{
				string text = uniqueKey.Replace("HDF_", "");
				if (list.Contains(text))
				{
					return text;
				}
			}
			return null;
		}

		public static string GetSteamID(Player player)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Expected O, but got Unknown
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//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_0079: 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_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
			ZNetView val = (ZNetView)GameManager.GetPrivateValue(player, "m_nview");
			string text = ((object)val.GetZDO()).ToString();
			Logger.Log("characterIDToFind = " + text);
			List<PlayerInfo> playerList = ZNet.instance.GetPlayerList();
			foreach (PlayerInfo item in playerList)
			{
				ZDOID characterID = item.m_characterID;
				Logger.Log("playerInfo.m_characterID.ToString() = " + ((object)(ZDOID)(ref characterID)).ToString());
				characterID = item.m_characterID;
				if (((object)(ZDOID)(ref characterID)).ToString() == text)
				{
					Logger.Log("Match characterIDToFind!");
					string host = item.m_host;
					Logger.Log("steamID found: " + host);
					return host;
				}
			}
			return "";
		}

		public static ProfessionData GetProfessionData(Recipe recipe)
		{
			Logger.Log("recipe.name " + ((Object)recipe).name + " firstPart " + ((Object)recipe).name.Split(new char[1] { ' ' })[0]);
			ProfessionData value;
			return RecipeManager.recipesProfessionData.TryGetValue(((Object)recipe).name.Split(new char[1] { ' ' })[0], out value) ? value : null;
		}

		public static bool IsPlayerLocalAdmin()
		{
			if ((Object)(object)ZNet.instance == (Object)null)
			{
				Logger.LogWarning("ZNet is not initialized in IsAdmin. returning false.");
				return false;
			}
			try
			{
				return ZNet.instance.LocalPlayerIsAdminOrHost();
			}
			catch (Exception s)
			{
				Logger.LogWarning(s);
			}
			return false;
		}

		public static bool IsAdmin(string steamId)
		{
			if ((Object)(object)ZNet.instance == (Object)null)
			{
				Logger.LogWarning("ZNet is not initialized in IsAdmin. returning false.");
				return false;
			}
			try
			{
				return ZNet.instance.IsAdmin(steamId);
			}
			catch (Exception s)
			{
				Logger.LogWarning(s);
			}
			return false;
		}
	}
	public class RecipeManager
	{
		public static Dictionary<string, ProfessionData> recipesProfessionData = new Dictionary<string, ProfessionData>();

		public static ProfessionData FindRecipeByItemName(string itemName)
		{
			foreach (ProfessionData value in recipesProfessionData.Values)
			{
				if (value.ItemName == itemName)
				{
					return value;
				}
			}
			return null;
		}
	}
}
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.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]
	[Microsoft.CodeAnalysis.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]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace ServerSync
{
	[PublicAPI]
	internal abstract class OwnConfigEntryBase
	{
		public object? LocalBaseValue;

		public bool SynchronizedConfig = true;

		public abstract ConfigEntryBase BaseConfig { get; }
	}
	[PublicAPI]
	internal class SyncedConfigEntry<T> : OwnConfigEntryBase
	{
		public readonly ConfigEntry<T> SourceConfig;

		public override ConfigEntryBase BaseConfig => (ConfigEntryBase)(object)SourceConfig;

		public T Value
		{
			get
			{
				return SourceConfig.Value;
			}
			set
			{
				SourceConfig.Value = value;
			}
		}

		public SyncedConfigEntry(ConfigEntry<T> sourceConfig)
		{
			SourceConfig = sourceConfig;
		}

		public void AssignLocalValue(T value)
		{
			if (LocalBaseValue == null)
			{
				Value = value;
			}
			else
			{
				LocalBaseValue = value;
			}
		}
	}
	internal abstract class CustomSyncedValueBase
	{
		public object? LocalBaseValue;

		public readonly string Identifier;

		public readonly Type Type;

		private object? boxedValue;

		protected bool localIsOwner;

		public readonly int Priority;

		public object? BoxedValue
		{
			get
			{
				return boxedValue;
			}
			set
			{
				boxedValue = value;
				this.ValueChanged?.Invoke();
			}
		}

		public event Action? ValueChanged;

		protected CustomSyncedValueBase(ConfigSync configSync, string identifier, Type type, int priority)
		{
			Priority = priority;
			Identifier = identifier;
			Type = type;
			configSync.AddCustomValue(this);
			localIsOwner = configSync.IsSourceOfTruth;
			configSync.SourceOfTruthChanged += delegate(bool truth)
			{
				localIsOwner = truth;
			};
		}
	}
	[PublicAPI]
	internal sealed class CustomSyncedValue<T> : CustomSyncedValueBase
	{
		public T Value
		{
			get
			{
				return (T)base.BoxedValue;
			}
			set
			{
				base.BoxedValue = value;
			}
		}

		public CustomSyncedValue(ConfigSync configSync, string identifier, T value = default(T), int priority = 0)
			: base(configSync, identifier, typeof(T), priority)
		{
			Value = value;
		}

		public void AssignLocalValue(T value)
		{
			if (localIsOwner)
			{
				Value = value;
			}
			else
			{
				LocalBaseValue = value;
			}
		}
	}
	internal class ConfigurationManagerAttributes
	{
		[UsedImplicitly]
		public bool? ReadOnly = false;
	}
	[PublicAPI]
	internal class ConfigSync
	{
		[HarmonyPatch(typeof(ZRpc), "HandlePackage")]
		private static class SnatchCurrentlyHandlingRPC
		{
			public static ZRpc? currentRpc;

			[HarmonyPrefix]
			private static void Prefix(ZRpc __instance)
			{
				currentRpc = __instance;
			}
		}

		[HarmonyPatch(typeof(ZNet), "Awake")]
		internal static class RegisterRPCPatch
		{
			[HarmonyPostfix]
			private static void Postfix(ZNet __instance)
			{
				isServer = __instance.IsServer();
				foreach (ConfigSync configSync2 in configSyncs)
				{
					ZRoutedRpc.instance.Register<ZPackage>(configSync2.Name + " ConfigSync", (Action<long, ZPackage>)configSync2.RPC_FromOtherClientConfigSync);
					if (isServer)
					{
						configSync2.InitialSyncDone = true;
						Debug.Log((object)("Registered '" + configSync2.Name + " ConfigSync' RPC - waiting for incoming connections"));
					}
				}
				if (isServer)
				{
					((MonoBehaviour)__instance).StartCoroutine(WatchAdminListChanges());
				}
				static void SendAdmin(List<ZNetPeer> peers, bool isAdmin)
				{
					ZPackage package = ConfigsToPackage(null, null, new PackageEntry[1]
					{
						new PackageEntry
						{
							section = "Internal",
							key = "lockexempt",
							type = typeof(bool),
							value = isAdmin
						}
					});
					ConfigSync configSync = configSyncs.First();
					if (configSync != null)
					{
						((MonoBehaviour)ZNet.instance).StartCoroutine(configSync.sendZPackage(peers, package));
					}
				}
				static IEnumerator WatchAdminListChanges()
				{
					MethodInfo listContainsId = AccessTools.DeclaredMethod(typeof(ZNet), "ListContainsId", (Type[])null, (Type[])null);
					SyncedList adminList = (SyncedList)AccessTools.DeclaredField(typeof(ZNet), "m_adminList").GetValue(ZNet.instance);
					List<string> CurrentList = new List<string>(adminList.GetList());
					while (true)
					{
						yield return (object)new WaitForSeconds(30f);
						if (!adminList.GetList().SequenceEqual(CurrentList))
						{
							CurrentList = new List<string>(adminList.GetList());
							List<ZNetPeer> adminPeer = ZNet.instance.GetPeers().Where(delegate(ZNetPeer p)
							{
								string hostName = p.m_rpc.GetSocket().GetHostName();
								return ((object)listContainsId == null) ? adminList.Contains(hostName) : ((bool)listContainsId.Invoke(ZNet.instance, new object[2] { adminList, hostName }));
							}).ToList();
							List<ZNetPeer> nonAdminPeer = ZNet.instance.GetPeers().Except(adminPeer).ToList();
							SendAdmin(nonAdminPeer, isAdmin: false);
							SendAdmin(adminPeer, isAdmin: true);
						}
					}
				}
			}
		}

		[HarmonyPatch(typeof(ZNet), "OnNewConnection")]
		private static class RegisterClientRPCPatch
		{
			[HarmonyPostfix]
			private static void Postfix(ZNet __instance, ZNetPeer peer)
			{
				if (__instance.IsServer())
				{
					return;
				}
				foreach (ConfigSync configSync in configSyncs)
				{
					peer.m_rpc.Register<ZPackage>(configSync.Name + " ConfigSync", (Action<ZRpc, ZPackage>)configSync.RPC_FromServerConfigSync);
				}
			}
		}

		private class ParsedConfigs
		{
			public readonly Dictionary<OwnConfigEntryBase, object?> configValues = new Dictionary<OwnConfigEntryBase, object>();

			public readonly Dictionary<CustomSyncedValueBase, object?> customValues = new Dictionary<CustomSyncedValueBase, object>();
		}

		[HarmonyPatch(typeof(ZNet), "Shutdown")]
		private class ResetConfigsOnShutdown
		{
			[HarmonyPostfix]
			private static void Postfix()
			{
				ProcessingServerUpdate = true;
				foreach (ConfigSync configSync in configSyncs)
				{
					configSync.resetConfigsFromServer();
					configSync.IsSourceOfTruth = true;
					configSync.InitialSyncDone = false;
				}
				ProcessingServerUpdate = false;
			}
		}

		[HarmonyPatch(typeof(ZNet), "RPC_PeerInfo")]
		private class SendConfigsAfterLogin
		{
			private class BufferingSocket : ISocket
			{
				public volatile bool finished = false;

				public volatile int versionMatchQueued = -1;

				public readonly List<ZPackage> Package = new List<ZPackage>();

				public readonly ISocket Original;

				public BufferingSocket(ISocket original)
				{
					Original = original;
				}

				public bool IsConnected()
				{
					return Original.IsConnected();
				}

				public ZPackage Recv()
				{
					return Original.Recv();
				}

				public int GetSendQueueSize()
				{
					return Original.GetSendQueueSize();
				}

				public int GetCurrentSendRate()
				{
					return Original.GetCurrentSendRate();
				}

				public bool IsHost()
				{
					return Original.IsHost();
				}

				public void Dispose()
				{
					Original.Dispose();
				}

				public bool GotNewData()
				{
					return Original.GotNewData();
				}

				public void Close()
				{
					Original.Close();
				}

				public string GetEndPointString()
				{
					return Original.GetEndPointString();
				}

				public void GetAndResetStats(out int totalSent, out int totalRecv)
				{
					Original.GetAndResetStats(ref totalSent, ref totalRecv);
				}

				public void GetConnectionQuality(out float localQuality, out float remoteQuality, out int ping, out float outByteSec, out float inByteSec)
				{
					Original.GetConnectionQuality(ref localQuality, ref remoteQuality, ref ping, ref outByteSec, ref inByteSec);
				}

				public ISocket Accept()
				{
					return Original.Accept();
				}

				public int GetHostPort()
				{
					return Original.GetHostPort();
				}

				public bool Flush()
				{
					return Original.Flush();
				}

				public string GetHostName()
				{
					return Original.GetHostName();
				}

				public void VersionMatch()
				{
					if (finished)
					{
						Original.VersionMatch();
					}
					else
					{
						versionMatchQueued = Package.Count;
					}
				}

				public void Send(ZPackage pkg)
				{
					//IL_0057: Unknown result type (might be due to invalid IL or missing references)
					//IL_005d: Expected O, but got Unknown
					int pos = pkg.GetPos();
					pkg.SetPos(0);
					int num = pkg.ReadInt();
					if ((num == StringExtensionMethods.GetStableHashCode("PeerInfo") || num == StringExtensionMethods.GetStableHashCode("RoutedRPC") || num == StringExtensionMethods.GetStableHashCode("ZDOData")) && !finished)
					{
						ZPackage val = new ZPackage(pkg.GetArray());
						val.SetPos(pos);
						Package.Add(val);
					}
					else
					{
						pkg.SetPos(pos);
						Original.Send(pkg);
					}
				}
			}

			[HarmonyPriority(800)]
			[HarmonyPrefix]
			private static void Prefix(ref Dictionary<Assembly, BufferingSocket>? __state, ZNet __instance, ZRpc rpc)
			{
				//IL_0078: Unknown result type (might be due to invalid IL or missing references)
				//IL_007e: Invalid comparison between Unknown and I4
				if (__instance.IsServer())
				{
					BufferingSocket value = new BufferingSocket(rpc.GetSocket());
					AccessTools.DeclaredField(typeof(ZRpc), "m_socket").SetValue(rpc, value);
					object? obj = AccessTools.DeclaredMethod(typeof(ZNet), "GetPeer", new Type[1] { typeof(ZRpc) }, (Type[])null).Invoke(__instance, new object[1] { rpc });
					ZNetPeer val = (ZNetPeer)((obj is ZNetPeer) ? obj : null);
					if (val != null && (int)ZNet.m_onlineBackend > 0)
					{
						AccessTools.DeclaredField(typeof(ZNetPeer), "m_socket").SetValue(val, value);
					}
					if (__state == null)
					{
						__state = new Dictionary<Assembly, BufferingSocket>();
					}
					__state[Assembly.GetExecutingAssembly()] = value;
				}
			}

			[HarmonyPostfix]
			private static void Postfix(Dictionary<Assembly, BufferingSocket> __state, ZNet __instance, ZRpc rpc)
			{
				ZRpc rpc2 = rpc;
				ZNet __instance2 = __instance;
				Dictionary<Assembly, BufferingSocket> __state2 = __state;
				ZNetPeer peer;
				if (__instance2.IsServer())
				{
					object obj = AccessTools.DeclaredMethod(typeof(ZNet), "GetPeer", new Type[1] { typeof(ZRpc) }, (Type[])null).Invoke(__instance2, new object[1] { rpc2 });
					peer = (ZNetPeer)((obj is ZNetPeer) ? obj : null);
					if (peer == null)
					{
						SendBufferedData();
					}
					else
					{
						((MonoBehaviour)__instance2).StartCoroutine(sendAsync());
					}
				}
				void SendBufferedData()
				{
					if (rpc2.GetSocket() is BufferingSocket bufferingSocket)
					{
						AccessTools.DeclaredField(typeof(ZRpc), "m_socket").SetValue(rpc2, bufferingSocket.Original);
						object? obj2 = AccessTools.DeclaredMethod(typeof(ZNet), "GetPeer", new Type[1] { typeof(ZRpc) }, (Type[])null).Invoke(__instance2, new object[1] { rpc2 });
						ZNetPeer val = (ZNetPeer)((obj2 is ZNetPeer) ? obj2 : null);
						if (val != null)
						{
							AccessTools.DeclaredField(typeof(ZNetPeer), "m_socket").SetValue(val, bufferingSocket.Original);
						}
					}
					BufferingSocket bufferingSocket2 = __state2[Assembly.GetExecutingAssembly()];
					bufferingSocket2.finished = true;
					for (int i = 0; i < bufferingSocket2.Package.Count; i++)
					{
						if (i == bufferingSocket2.versionMatchQueued)
						{
							bufferingSocket2.Original.VersionMatch();
						}
						bufferingSocket2.Original.Send(bufferingSocket2.Package[i]);
					}
					if (bufferingSocket2.Package.Count == bufferingSocket2.versionMatchQueued)
					{
						bufferingSocket2.Original.VersionMatch();
					}
				}
				IEnumerator sendAsync()
				{
					foreach (ConfigSync configSync in configSyncs)
					{
						List<PackageEntry> entries = new List<PackageEntry>();
						if (configSync.CurrentVersion != null)
						{
							entries.Add(new PackageEntry
							{
								section = "Internal",
								key = "serverversion",
								type = typeof(string),
								value = configSync.CurrentVersion
							});
						}
						MethodInfo listContainsId = AccessTools.DeclaredMethod(typeof(ZNet), "ListContainsId", (Type[])null, (Type[])null);
						SyncedList adminList = (SyncedList)AccessTools.DeclaredField(typeof(ZNet), "m_adminList").GetValue(ZNet.instance);
						entries.Add(new PackageEntry
						{
							section = "Internal",
							key = "lockexempt",
							type = typeof(bool),
							value = (((object)listContainsId == null) ? ((object)adminList.Contains(rpc2.GetSocket().GetHostName())) : listContainsId.Invoke(ZNet.instance, new object[2]
							{
								adminList,
								rpc2.GetSocket().GetHostName()
							}))
						});
						ZPackage package = ConfigsToPackage(configSync.allConfigs.Select((OwnConfigEntryBase c) => c.BaseConfig), configSync.allCustomValues, entries, partial: false);
						yield return ((MonoBehaviour)__instance2).StartCoroutine(configSync.sendZPackage(new List<ZNetPeer> { peer }, package));
					}
					SendBufferedData();
				}
			}
		}

		private class PackageEntry
		{
			public string section = null;

			public string key = null;

			public Type type = null;

			public object? value;
		}

		[HarmonyPatch(typeof(ConfigEntryBase), "GetSerializedValue")]
		private static class PreventSavingServerInfo
		{
			[HarmonyPrefix]
			private static bool Prefix(ConfigEntryBase __instance, ref string __result)
			{
				OwnConfigEntryBase ownConfigEntryBase = configData(__instance);
				if (ownConfigEntryBase == null || isWritableConfig(ownConfigEntryBase))
				{
					return true;
				}
				__result = TomlTypeConverter.ConvertToString(ownConfigEntryBase.LocalBaseValue, __instance.SettingType);
				return false;
			}
		}

		[HarmonyPatch(typeof(ConfigEntryBase), "SetSerializedValue")]
		private static class PreventConfigRereadChangingValues
		{
			[HarmonyPrefix]
			private static bool Prefix(ConfigEntryBase __instance, string value)
			{
				OwnConfigEntryBase ownConfigEntryBase = configData(__instance);
				if (ownConfigEntryBase == null || ownConfigEntryBase.LocalBaseValue == null)
				{
					return true;
				}
				try
				{
					ownConfigEntryBase.LocalBaseValue = TomlTypeConverter.ConvertToValue(value, __instance.SettingType);
				}
				catch (Exception ex)
				{
					Debug.LogWarning((object)$"Config value of setting \"{__instance.Definition}\" could not be parsed and will be ignored. Reason: {ex.Message}; Value: {value}");
				}
				return false;
			}
		}

		private class InvalidDeserializationTypeException : Exception
		{
			public string expected = null;

			public string received = null;

			public string field = "";
		}

		public static bool ProcessingServerUpdate;

		public readonly string Name;

		public string? DisplayName;

		public string? CurrentVersion;

		public string? MinimumRequiredVersion;

		public bool ModRequired = false;

		private bool? forceConfigLocking;

		private bool isSourceOfTruth = true;

		private static readonly HashSet<ConfigSync> configSyncs;

		private readonly HashSet<OwnConfigEntryBase> allConfigs = new HashSet<OwnConfigEntryBase>();

		private HashSet<CustomSyncedValueBase> allCustomValues = new HashSet<CustomSyncedValueBase>();

		private static bool isServer;

		private static bool lockExempt;

		private OwnConfigEntryBase? lockedConfig = null;

		private const byte PARTIAL_CONFIGS = 1;

		private const byte FRAGMENTED_CONFIG = 2;

		private const byte COMPRESSED_CONFIG = 4;

		private readonly Dictionary<string, SortedDictionary<int, byte[]>> configValueCache = new Dictionary<string, SortedDictionary<int, byte[]>>();

		private readonly List<KeyValuePair<long, string>> cacheExpirations = new List<KeyValuePair<long, string>>();

		private static long packageCounter;

		public bool IsLocked
		{
			get
			{
				bool? flag = forceConfigLocking;
				bool num;
				if (!flag.HasValue)
				{
					if (lockedConfig == null)
					{
						goto IL_0052;
					}
					num = ((IConvertible)lockedConfig.BaseConfig.BoxedValue).ToInt32(CultureInfo.InvariantCulture) != 0;
				}
				else
				{
					num = flag.GetValueOrDefault();
				}
				if (!num)
				{
					goto IL_0052;
				}
				int result = ((!lockExempt) ? 1 : 0);
				goto IL_0053;
				IL_0053:
				return (byte)result != 0;
				IL_0052:
				result = 0;
				goto IL_0053;
			}
			set
			{
				forceConfigLocking = value;
			}
		}

		public bool IsAdmin => lockExempt || isSourceOfTruth;

		public bool IsSourceOfTruth
		{
			get
			{
				return isSourceOfTruth;
			}
			private set
			{
				if (value != isSourceOfTruth)
				{
					isSourceOfTruth = value;
					this.SourceOfTruthChanged?.Invoke(value);
				}
			}
		}

		public bool InitialSyncDone { get; private set; } = false;


		public event Action<bool>? SourceOfTruthChanged;

		private event Action? lockedConfigChanged;

		static ConfigSync()
		{
			ProcessingServerUpdate = false;
			configSyncs = new HashSet<ConfigSync>();
			lockExempt = false;
			packageCounter = 0L;
			RuntimeHelpers.RunClassConstructor(typeof(VersionCheck).TypeHandle);
		}

		public ConfigSync(string name)
		{
			Name = name;
			configSyncs.Add(this);
			new VersionCheck(this);
		}

		public SyncedConfigEntry<T> AddConfigEntry<T>(ConfigEntry<T> configEntry)
		{
			ConfigEntry<T> configEntry2 = configEntry;
			OwnConfigEntryBase ownConfigEntryBase = configData((ConfigEntryBase)(object)configEntry2);
			SyncedConfigEntry<T> syncedEntry = ownConfigEntryBase as SyncedConfigEntry<T>;
			if (syncedEntry == null)
			{
				syncedEntry = new SyncedConfigEntry<T>(configEntry2);
				AccessTools.DeclaredField(typeof(ConfigDescription), "<Tags>k__BackingField").SetValue(((ConfigEntryBase)configEntry2).Description, new object[1]
				{
					new ConfigurationManagerAttributes()
				}.Concat(((ConfigEntryBase)configEntry2).Description.Tags ?? Array.Empty<object>()).Concat(new SyncedConfigEntry<T>[1] { syncedEntry }).ToArray());
				configEntry2.SettingChanged += delegate
				{
					if (!ProcessingServerUpdate && syncedEntry.SynchronizedConfig)
					{
						Broadcast(ZRoutedRpc.Everybody, (ConfigEntryBase)configEntry2);
					}
				};
				allConfigs.Add(syncedEntry);
			}
			return syncedEntry;
		}

		public SyncedConfigEntry<T> AddLockingConfigEntry<T>(ConfigEntry<T> lockingConfig) where T : IConvertible
		{
			if (lockedConfig != null)
			{
				throw new Exception("Cannot initialize locking ConfigEntry twice");
			}
			lockedConfig = AddConfigEntry<T>(lockingConfig);
			lockingConfig.SettingChanged += delegate
			{
				this.lockedConfigChanged?.Invoke();
			};
			return (SyncedConfigEntry<T>)lockedConfig;
		}

		internal void AddCustomValue(CustomSyncedValueBase customValue)
		{
			CustomSyncedValueBase customValue2 = customValue;
			if (allCustomValues.Select((CustomSyncedValueBase v) => v.Identifier).Concat(new string[1] { "serverversion" }).Contains(customValue2.Identifier))
			{
				throw new Exception("Cannot have multiple settings with the same name or with a reserved name (serverversion)");
			}
			allCustomValues.Add(customValue2);
			allCustomValues = new HashSet<CustomSyncedValueBase>(allCustomValues.OrderByDescending((CustomSyncedValueBase v) => v.Priority));
			customValue2.ValueChanged += delegate
			{
				if (!ProcessingServerUpdate)
				{
					Broadcast(ZRoutedRpc.Everybody, customValue2);
				}
			};
		}

		private void RPC_FromServerConfigSync(ZRpc rpc, ZPackage package)
		{
			lockedConfigChanged += serverLockedSettingChanged;
			IsSourceOfTruth = false;
			if (HandleConfigSyncRPC(0L, package, clientUpdate: false))
			{
				InitialSyncDone = true;
			}
		}

		private void RPC_FromOtherClientConfigSync(long sender, ZPackage package)
		{
			HandleConfigSyncRPC(sender, package, clientUpdate: true);
		}

		private bool HandleConfigSyncRPC(long sender, ZPackage package, bool clientUpdate)
		{
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Expected O, but got Unknown
			//IL_0250: Unknown result type (might be due to invalid IL or missing references)
			//IL_0257: Expected O, but got Unknown
			//IL_01ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f1: Expected O, but got Unknown
			try
			{
				if (isServer && IsLocked)
				{
					ZRpc? currentRpc = SnatchCurrentlyHandlingRPC.currentRpc;
					object obj;
					if (currentRpc == null)
					{
						obj = null;
					}
					else
					{
						ISocket socket = currentRpc.GetSocket();
						obj = ((socket != null) ? socket.GetHostName() : null);
					}
					string text = (string)obj;
					if (text != null)
					{
						MethodInfo methodInfo = AccessTools.DeclaredMethod(typeof(ZNet), "ListContainsId", (Type[])null, (Type[])null);
						SyncedList val = (SyncedList)AccessTools.DeclaredField(typeof(ZNet), "m_adminList").GetValue(ZNet.instance);
						if (!(((object)methodInfo == null) ? val.Contains(text) : ((bool)methodInfo.Invoke(ZNet.instance, new object[2] { val, text }))))
						{
							return false;
						}
					}
				}
				cacheExpirations.RemoveAll(delegate(KeyValuePair<long, string> kv)
				{
					if (kv.Key < DateTimeOffset.Now.Ticks)
					{
						configValueCache.Remove(kv.Value);
						return true;
					}
					return false;
				});
				byte b = package.ReadByte();
				if ((b & 2u) != 0)
				{
					long num = package.ReadLong();
					string text2 = sender.ToString() + num;
					if (!configValueCache.TryGetValue(text2, out SortedDictionary<int, byte[]> value))
					{
						value = new SortedDictionary<int, byte[]>();
						configValueCache[text2] = value;
						cacheExpirations.Add(new KeyValuePair<long, string>(DateTimeOffset.Now.AddSeconds(60.0).Ticks, text2));
					}
					int key = package.ReadInt();
					int num2 = package.ReadInt();
					value.Add(key, package.ReadByteArray());
					if (value.Count < num2)
					{
						return false;
					}
					configValueCache.Remove(text2);
					package = new ZPackage(value.Values.SelectMany((byte[] a) => a).ToArray());
					b = package.ReadByte();
				}
				ProcessingServerUpdate = true;
				if ((b & 4u) != 0)
				{
					byte[] buffer = package.ReadByteArray();
					MemoryStream stream = new MemoryStream(buffer);
					MemoryStream memoryStream = new MemoryStream();
					using (DeflateStream deflateStream = new DeflateStream(stream, CompressionMode.Decompress))
					{
						deflateStream.CopyTo(memoryStream);
					}
					package = new ZPackage(memoryStream.ToArray());
					b = package.ReadByte();
				}
				if ((b & 1) == 0)
				{
					resetConfigsFromServer();
				}
				ParsedConfigs parsedConfigs = ReadConfigsFromPackage(package);
				ConfigFile val2 = null;
				bool saveOnConfigSet = false;
				foreach (KeyValuePair<OwnConfigEntryBase, object> configValue in parsedConfigs.configValues)
				{
					if (!isServer && configValue.Key.LocalBaseValue == null)
					{
						configValue.Key.LocalBaseValue = configValue.Key.BaseConfig.BoxedValue;
					}
					if (val2 == null)
					{
						val2 = configValue.Key.BaseConfig.ConfigFile;
						saveOnConfigSet = val2.SaveOnConfigSet;
						val2.SaveOnConfigSet = false;
					}
					configValue.Key.BaseConfig.BoxedValue = configValue.Value;
				}
				if (val2 != null)
				{
					val2.SaveOnConfigSet = saveOnConfigSet;
				}
				foreach (KeyValuePair<CustomSyncedValueBase, object> customValue in parsedConfigs.customValues)
				{
					if (!isServer)
					{
						CustomSyncedValueBase key2 = customValue.Key;
						if (key2.LocalBaseValue == null)
						{
							key2.LocalBaseValue = customValue.Key.BoxedValue;
						}
					}
					customValue.Key.BoxedValue = customValue.Value;
				}
				Debug.Log((object)string.Format("Received {0} configs and {1} custom values from {2} for mod {3}", parsedConfigs.configValues.Count, parsedConfigs.customValues.Count, (isServer || clientUpdate) ? $"client {sender}" : "the server", DisplayName ?? Name));
				if (!isServer)
				{
					serverLockedSettingChanged();
				}
				return true;
			}
			finally
			{
				ProcessingServerUpdate = false;
			}
		}

		private ParsedConfigs ReadConfigsFromPackage(ZPackage package)
		{
			ParsedConfigs parsedConfigs = new ParsedConfigs();
			Dictionary<string, OwnConfigEntryBase> dictionary = allConfigs.Where((OwnConfigEntryBase c) => c.SynchronizedConfig).ToDictionary((OwnConfigEntryBase c) => c.BaseConfig.Definition.Section + "_" + c.BaseConfig.Definition.Key, (OwnConfigEntryBase c) => c);
			Dictionary<string, CustomSyncedValueBase> dictionary2 = allCustomValues.ToDictionary((CustomSyncedValueBase c) => c.Identifier, (CustomSyncedValueBase c) => c);
			int num = package.ReadInt();
			for (int i = 0; i < num; i++)
			{
				string text = package.ReadString();
				string text2 = package.ReadString();
				string text3 = package.ReadString();
				Type type = Type.GetType(text3);
				if (text3 == "" || type != null)
				{
					object obj;
					try
					{
						obj = ((text3 == "") ? null : ReadValueWithTypeFromZPackage(package, type));
					}
					catch (InvalidDeserializationTypeException ex)
					{
						Debug.LogWarning((object)("Got unexpected struct internal type " + ex.received + " for field " + ex.field + " struct " + text3 + " for " + text2 + " in section " + text + " for mod " + (DisplayName ?? Name) + ", expecting " + ex.expected));
						continue;
					}
					OwnConfigEntryBase value2;
					if (text == "Internal")
					{
						CustomSyncedValueBase value;
						if (text2 == "s