Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of HIJOS DE FREYJA ROLEPLAY v0.0.6
Bepinex/plugins/CraftByProfession/CraftByProfession.dll
Decompiled 2 years 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