Decompiled source of HIJOS DE FREYJA ROLEPLAY v0.0.6
Bepinex/plugins/CraftByProfession/CraftByProfession.dll
Decompiled 3 weeks ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.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