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 VeiledRecipes v1.0.1
VeiledRecipes.dll
Decompiled a day 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.Security; using System.Security.Permissions; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using JetBrains.Annotations; using Microsoft.CodeAnalysis; using ServerSync; using TMPro; using UnityEngine; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyTitle("VeiledRecipes")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("sighsorry")] [assembly: AssemblyProduct("VeiledRecipes")] [assembly: AssemblyCopyright("Copyright © 2021")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("4358610B-F3F4-4843-B7AF-98B7BC60DCDE")] [assembly: AssemblyFileVersion("1.0.1")] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.1.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] 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; } } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class ExtensionMarkerAttribute : Attribute { private readonly string <Name>k__BackingField; public string Name => <Name>k__BackingField; public ExtensionMarkerAttribute(string name) { <Name>k__BackingField = name; } } } namespace VeiledRecipes { [BepInPlugin("sighsorry.VeiledRecipes", "VeiledRecipes", "1.0.1")] public class VeiledRecipesPlugin : BaseUnityPlugin { public enum Toggle { On = 1, Off = 0 } private class ConfigurationManagerAttributes { [UsedImplicitly] public int? Order; [UsedImplicitly] public bool? Browsable; [UsedImplicitly] public string? Category; [UsedImplicitly] public Action<ConfigEntryBase>? CustomDrawer; } private class AcceptableShortcuts : AcceptableValueBase { public AcceptableShortcuts() : base(typeof(KeyboardShortcut)) { } public override object Clamp(object value) { return value; } public override bool IsValid(object value) { return true; } public override string ToDescriptionString() { return "# Acceptable values: " + string.Join(", ", UnityInput.Current.SupportedKeyCodes); } } internal const string ModName = "VeiledRecipes"; internal const string ModVersion = "1.0.1"; internal const string Author = "sighsorry"; public const string ModGUID = "sighsorry.VeiledRecipes"; private static string ConfigFileName = "sighsorry.VeiledRecipes.cfg"; private static string ConfigFileFullPath; internal static string ConnectionError; private readonly Harmony _harmony = new Harmony("sighsorry.VeiledRecipes"); public static readonly ManualLogSource PluginLogger; private static readonly ConfigSync ConfigSync; private FileSystemWatcher? _watcher; private readonly object _reloadLock = new object(); private DateTime _lastConfigReloadTime; private static readonly TimeSpan ConfigReloadDebounce; private static ConfigEntry<Toggle> _serverConfigLocked; internal static ConfigEntry<Toggle> ShowUnknownCraftingRecipes; internal static ConfigEntry<Toggle> ShowUnknownBuildPieces; internal static ConfigEntry<Toggle> RequireStationLevelForUnknownCraftingRecipes; internal static ConfigEntry<Toggle> RequireStationKnowledgeForUnknownBuildPieces; internal static ConfigEntry<Toggle> RequireStationInteractionForRecipeUnlock; internal static ConfigEntry<Toggle> EnableStationProximityDiscovery; internal static ConfigEntry<string> RecipePreviewPrefabBlacklist; internal static ConfigEntry<string> PiecePreviewPrefabBlacklist; internal static ConfigEntry<string> RequirementPreviewPrefabBlacklist; internal static ConfigEntry<string> UnknownNameText; internal static ConfigEntry<string> UnknownDescriptionText; internal static ConfigEntry<string> UnknownRequirementText; internal static ConfigEntry<bool> GroupUnknownRecipePreviewsBelowKnownRecipes; internal static ConfigEntry<bool> ShowRecipeUnlockNotifications; internal static ConfigEntry<bool> ShowPieceUnlockNotifications; internal static ConfigEntry<bool> ShowSkillLevelUpNotificationAndEffect; public void Awake() { bool saveOnConfigSet = ((BaseUnityPlugin)this).Config.SaveOnConfigSet; ((BaseUnityPlugin)this).Config.SaveOnConfigSet = false; _serverConfigLocked = config("1 - General", "Lock Configuration", Toggle.On, "If on, the configuration is locked and can be changed by server admins only."); ConfigSync.AddLockingConfigEntry<Toggle>(_serverConfigLocked); ShowUnknownCraftingRecipes = config("2 - Veiled Recipes", "Show Unknown Crafting Recipes", Toggle.On, "Shows crafting recipes at the relevant crafting station before the recipe is fully unlocked."); ShowUnknownBuildPieces = config("2 - Veiled Recipes", "Show Unknown Build Pieces", Toggle.On, "Shows build pieces in build-piece tables before the piece is fully unlocked."); RequireStationLevelForUnknownCraftingRecipes = config("2 - Veiled Recipes", "Require Station Level For Unknown Crafting Recipes", Toggle.On, "If on, unknown crafting recipe previews are shown only when the current crafting station meets the recipe's required station level."); RequireStationKnowledgeForUnknownBuildPieces = config("2 - Veiled Recipes", "Require Station Knowledge For Unknown Build Pieces", Toggle.On, "If on, unknown build-piece previews that require a crafting station are shown only after the player knows that station."); RequireStationInteractionForRecipeUnlock = config("2 - Veiled Recipes", "Require Station Interaction For Recipe Unlock", Toggle.On, "If on, recipes that require a crafting station unlock only after the player has interacted with the required station level. If off, Valheim's normal station discovery behavior is used for recipe station knowledge."); EnableStationProximityDiscovery = config("2 - Veiled Recipes", "Enable Station Proximity Discovery", Toggle.On, "If on, Valheim's normal crafting station discovery radius is used. If off, walking near a crafting station does not discover it; interacting with the station is required."); RecipePreviewPrefabBlacklist = config("2 - Veiled Recipes", "Recipe Preview Prefab Blacklist", "", "Comma-separated item prefab names whose unknown crafting recipe previews should never be shown. This does not hide recipes after they are actually unlocked. Example: ArmorIronLegs, SwordIron"); PiecePreviewPrefabBlacklist = config("2 - Veiled Recipes", "Piece Preview Prefab Blacklist", "", "Comma-separated piece prefab names whose unknown build piece previews should never be shown. This does not hide pieces after they are actually unlocked. Example: piece_workbench_ext1, piece_chest"); RequirementPreviewPrefabBlacklist = config("2 - Veiled Recipes", "Requirement Preview Prefab Blacklist", "SwordCheat, SledgeCheat", "Comma-separated ingredient/resource prefab names that prevent unknown crafting recipe and build-piece previews from being shown when required by that recipe or piece. This does not hide anything after it is actually unlocked."); UnknownNameText = config("3 - Display", "Unknown Name Text", "???", "Text shown for unknown recipe and piece names."); UnknownDescriptionText = config("3 - Display", "Unknown Description Text", "Not enough info", "Text shown for unknown recipe and piece descriptions."); UnknownRequirementText = config("3 - Display", "Unknown Requirement Text", "?", "Text shown for unknown requirement names, amounts, and station levels."); GroupUnknownRecipePreviewsBelowKnownRecipes = config("3 - Display", "Group Unknown Recipe Previews Below Known Recipes", value: true, "If enabled, unknown crafting recipe previews are grouped below actually unlocked recipes in crafting station recipe lists.", synchronizedSetting: false); ShowRecipeUnlockNotifications = config("4 - Client Notifications", "Show Recipe Unlock Notifications", value: true, "Shows Valheim's unlock popup when a crafting recipe is learned.", synchronizedSetting: false); ShowPieceUnlockNotifications = config("4 - Client Notifications", "Show Piece Unlock Notifications", value: false, "Shows Valheim's unlock popup when a build piece or dish is learned.", synchronizedSetting: false); ShowSkillLevelUpNotificationAndEffect = config("4 - Client Notifications", "Show Skill Level Up Notification/Effect", value: true, "Shows the skill level-up message and VFX/SFX. When disabled, all $msg_skillup alarms and Player.OnSkillLevelup effects are hidden.", synchronizedSetting: false); Assembly executingAssembly = Assembly.GetExecutingAssembly(); _harmony.PatchAll(executingAssembly); SetupWatcher(); ((BaseUnityPlugin)this).Config.Save(); if (saveOnConfigSet) { ((BaseUnityPlugin)this).Config.SaveOnConfigSet = saveOnConfigSet; } } private void OnDestroy() { SaveWithRespectToConfigSet(); _watcher?.Dispose(); } private void SetupWatcher() { _watcher = new FileSystemWatcher(Paths.ConfigPath, ConfigFileName); _watcher.Changed += ReadConfigValues; _watcher.Created += ReadConfigValues; _watcher.Renamed += ReadConfigValues; _watcher.IncludeSubdirectories = true; _watcher.SynchronizingObject = ThreadingHelper.SynchronizingObject; _watcher.EnableRaisingEvents = true; } private void ReadConfigValues(object sender, FileSystemEventArgs e) { DateTime now = DateTime.Now; if (now - _lastConfigReloadTime < ConfigReloadDebounce) { return; } lock (_reloadLock) { if (!File.Exists(ConfigFileFullPath)) { PluginLogger.LogWarning((object)"Config file does not exist. Skipping reload."); return; } try { PluginLogger.LogDebug((object)"Reloading configuration..."); SaveWithRespectToConfigSet(reload: true); PluginLogger.LogInfo((object)"Configuration reload complete."); } catch (Exception ex) { PluginLogger.LogError((object)("Error reloading configuration: " + ex.Message)); } } _lastConfigReloadTime = now; } private void SaveWithRespectToConfigSet(bool reload = false) { bool saveOnConfigSet = ((BaseUnityPlugin)this).Config.SaveOnConfigSet; ((BaseUnityPlugin)this).Config.SaveOnConfigSet = false; if (reload) { ((BaseUnityPlugin)this).Config.Reload(); } ((BaseUnityPlugin)this).Config.Save(); if (saveOnConfigSet) { ((BaseUnityPlugin)this).Config.SaveOnConfigSet = saveOnConfigSet; } } private ConfigEntry<T> config<T>(string group, string name, T value, ConfigDescription description, bool synchronizedSetting = true) { //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0030: 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 = ((BaseUnityPlugin)this).Config.Bind<T>(group, name, value, val); ConfigSync.AddConfigEntry<T>(val2).SynchronizedConfig = synchronizedSetting; return val2; } private ConfigEntry<T> config<T>(string group, string name, T value, string description, bool synchronizedSetting = true) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Expected O, but got Unknown return config(group, name, value, new ConfigDescription(description, (AcceptableValueBase)null, Array.Empty<object>()), synchronizedSetting); } static VeiledRecipesPlugin() { string configPath = Paths.ConfigPath; char directorySeparatorChar = Path.DirectorySeparatorChar; ConfigFileFullPath = configPath + directorySeparatorChar + ConfigFileName; ConnectionError = ""; PluginLogger = Logger.CreateLogSource("VeiledRecipes"); ConfigSync = new ConfigSync("sighsorry.VeiledRecipes") { DisplayName = "VeiledRecipes", CurrentVersion = "1.0.1", MinimumRequiredVersion = "1.0.1" }; ConfigReloadDebounce = TimeSpan.FromSeconds(1.0); _serverConfigLocked = null; ShowUnknownCraftingRecipes = null; ShowUnknownBuildPieces = null; RequireStationLevelForUnknownCraftingRecipes = null; RequireStationKnowledgeForUnknownBuildPieces = null; RequireStationInteractionForRecipeUnlock = null; EnableStationProximityDiscovery = null; RecipePreviewPrefabBlacklist = null; PiecePreviewPrefabBlacklist = null; RequirementPreviewPrefabBlacklist = null; UnknownNameText = null; UnknownDescriptionText = null; UnknownRequirementText = null; GroupUnknownRecipePreviewsBelowKnownRecipes = null; ShowRecipeUnlockNotifications = null; ShowPieceUnlockNotifications = null; ShowSkillLevelUpNotificationAndEffect = null; } } public static class KeyboardExtensions { [SpecialName] public sealed class <G>$8D1D3E80A18AA9715780B6CB7003B2F1 { [SpecialName] public static class <M>$895AB635D4D087636CF1C26BA650BA11 { } [ExtensionMarker("<M>$895AB635D4D087636CF1C26BA650BA11")] public bool IsKeyDown() { throw new NotSupportedException(); } [ExtensionMarker("<M>$895AB635D4D087636CF1C26BA650BA11")] public bool IsKeyHeld() { throw new NotSupportedException(); } } public static bool IsKeyDown(this KeyboardShortcut shortcut) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) if ((int)((KeyboardShortcut)(ref shortcut)).MainKey != 0 && Input.GetKeyDown(((KeyboardShortcut)(ref shortcut)).MainKey)) { return ((KeyboardShortcut)(ref shortcut)).Modifiers.All((Func<KeyCode, bool>)Input.GetKey); } return false; } public static bool IsKeyHeld(this KeyboardShortcut shortcut) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) if ((int)((KeyboardShortcut)(ref shortcut)).MainKey != 0 && Input.GetKey(((KeyboardShortcut)(ref shortcut)).MainKey)) { return ((KeyboardShortcut)(ref shortcut)).Modifiers.All((Func<KeyCode, bool>)Input.GetKey); } return false; } } public static class ToggleExtentions { [SpecialName] public sealed class <G>$88DA5E60784AA7EB773CA00CCCD1F4F1 { [SpecialName] public static class <M>$7F648C9DF91AD5654B3C5099B6972F20 { } [ExtensionMarker("<M>$7F648C9DF91AD5654B3C5099B6972F20")] public bool IsOn() { throw new NotSupportedException(); } [ExtensionMarker("<M>$7F648C9DF91AD5654B3C5099B6972F20")] public bool IsOff() { throw new NotSupportedException(); } } public static bool IsOn(this VeiledRecipesPlugin.Toggle value) { return value == VeiledRecipesPlugin.Toggle.On; } public static bool IsOff(this VeiledRecipesPlugin.Toggle value) { return value == VeiledRecipesPlugin.Toggle.Off; } } internal static class VeiledRecipeConstants { internal const string StationInteractionPrefix = "VeiledRecipes.InteractedStation."; internal const string UnknownNameFallback = "???"; internal const string UnknownDescriptionFallback = "Not enough info"; internal const string UnknownRequirementFallback = "?"; internal const string NewRecipeMessage = "$msg_newrecipe"; internal const string NewPieceMessage = "$msg_newpiece"; internal const string NewDishMessage = "$msg_newdish"; internal const string SkillUpMessagePrefix = "$msg_skillup "; internal const string MissingRequirementMessage = "$msg_missingrequirement"; internal const string MenuNoneMessage = "$menu_none"; internal const string CloneSuffix = "(Clone)"; internal const int PieceCategoryBucketCount = 8; internal const string RecipeIconChild = "icon"; internal const string RecipeNameChild = "name"; internal const string DurabilityChild = "Durability"; internal const string QualityLevelChild = "QualityLevel"; internal const string RequirementIconChild = "res_icon"; internal const string RequirementNameChild = "res_name"; internal const string RequirementAmountChild = "res_amount"; internal static readonly char[] PrefabBlacklistSeparators = new char[5] { ',', ';', '|', '\n', '\r' }; } [HarmonyPatch(typeof(MessageHud), "QueueUnlockMsg")] internal static class MessageHudQueueUnlockMsgPatch { private static bool Prefix(MessageHud __instance, string topic, string description) { if (VeiledRecipeState.ShouldShowUnlockNotification(topic)) { return true; } __instance.AddLog(topic + ": " + description); return false; } } [HarmonyPatch(typeof(Player), "OnSkillLevelup")] internal static class PlayerSkillLevelUpEffectsPatch { private static bool Prefix() { return VeiledRecipeState.ShouldShowSkillLevelUpEffect(); } } [HarmonyPatch(typeof(Player), "Message")] internal static class PlayerSkillNotificationAlarmPatch { private static bool Prefix(string msg) { return VeiledRecipeState.ShouldShowSkillLevelUpNotification(msg); } } internal static class VeiledRecipeState { private static string _recipePreviewPrefabBlacklistRaw = ""; private static string _piecePreviewPrefabBlacklistRaw = ""; private static string _requirementPreviewPrefabBlacklistRaw = ""; private static HashSet<string> _recipePreviewPrefabBlacklist = new HashSet<string>(StringComparer.OrdinalIgnoreCase); private static HashSet<string> _piecePreviewPrefabBlacklist = new HashSet<string>(StringComparer.OrdinalIgnoreCase); private static HashSet<string> _requirementPreviewPrefabBlacklist = new HashSet<string>(StringComparer.OrdinalIgnoreCase); private static readonly List<Func<Piece, bool>> KnownPieceOverrides = new List<Func<Piece, bool>>(); private static readonly HashSet<string> KnownPiecePrefabOverrides = new HashSet<string>(StringComparer.OrdinalIgnoreCase); internal static bool ShowUnknownCraftingRecipes => IsOn(VeiledRecipesPlugin.ShowUnknownCraftingRecipes); internal static bool ShowUnknownBuildPieces => IsOn(VeiledRecipesPlugin.ShowUnknownBuildPieces); internal static bool RequireStationLevelForUnknownCraftingRecipes => IsOn(VeiledRecipesPlugin.RequireStationLevelForUnknownCraftingRecipes); internal static bool RequireStationKnowledgeForUnknownBuildPieces => IsOn(VeiledRecipesPlugin.RequireStationKnowledgeForUnknownBuildPieces); internal static bool RequireStationInteractionForRecipeUnlock => IsOn(VeiledRecipesPlugin.RequireStationInteractionForRecipeUnlock); internal static bool EnableStationProximityDiscovery => IsOn(VeiledRecipesPlugin.EnableStationProximityDiscovery); internal static string UnknownNameText => SafeText(VeiledRecipesPlugin.UnknownNameText, "???"); internal static string UnknownDescriptionText => SafeText(VeiledRecipesPlugin.UnknownDescriptionText, "Not enough info"); internal static string UnknownRequirementText => SafeText(VeiledRecipesPlugin.UnknownRequirementText, "?"); internal static bool GroupUnknownRecipePreviewsBelowKnownRecipes => VeiledRecipesPlugin.GroupUnknownRecipePreviewsBelowKnownRecipes?.Value ?? true; internal static bool ShowRecipeUnlockNotifications => VeiledRecipesPlugin.ShowRecipeUnlockNotifications?.Value ?? true; internal static bool ShowPieceUnlockNotifications => VeiledRecipesPlugin.ShowPieceUnlockNotifications?.Value ?? true; internal static bool ShowSkillLevelUpNotificationAndEffect => VeiledRecipesPlugin.ShowSkillLevelUpNotificationAndEffect?.Value ?? true; internal static bool ShouldShowUnlockNotification(string topic) { switch (NormalizeToken(topic)) { case "$msg_newrecipe": return ShowRecipeUnlockNotifications; case "$msg_newpiece": case "$msg_newdish": return ShowPieceUnlockNotifications; default: return true; } } internal static bool ShouldShowSkillLevelUpNotification(string message) { if (IsSkillNotificationMessage(message)) { return ShowSkillLevelUpNotificationAndEffect; } return true; } internal static bool ShouldShowSkillLevelUpEffect() { return ShowSkillLevelUpNotificationAndEffect; } private static bool IsSkillNotificationMessage(string message) { return NormalizeToken(message).StartsWith("$msg_skillup ", StringComparison.OrdinalIgnoreCase); } private static string NormalizeToken(string value) { return (value ?? "").Trim(); } private static bool IsOn(ConfigEntry<VeiledRecipesPlugin.Toggle> entry) { if (entry != null) { return entry.Value == VeiledRecipesPlugin.Toggle.On; } return false; } private static string SafeText(ConfigEntry<string> entry, string fallback) { if (entry == null || string.IsNullOrEmpty(entry.Value)) { return fallback; } return entry.Value; } private static bool IsRecipePreviewBlacklisted(Recipe recipe) { HashSet<string> recipePreviewPrefabBlacklist = GetRecipePreviewPrefabBlacklist(); if (recipePreviewPrefabBlacklist.Count > 0) { string[] obj = new string[4] { ((Object)recipe).name, null, null, null }; ItemDrop item = recipe.m_item; obj[1] = ((item != null) ? ((Object)item).name : null); ItemDrop item2 = recipe.m_item; object obj2; if (item2 == null) { obj2 = null; } else { GameObject gameObject = ((Component)item2).gameObject; obj2 = ((gameObject != null) ? ((Object)gameObject).name : null); } obj[2] = (string)obj2; ItemDrop item3 = recipe.m_item; object obj3; if (item3 == null) { obj3 = null; } else { GameObject dropPrefab = item3.m_itemData.m_dropPrefab; obj3 = ((dropPrefab != null) ? ((Object)dropPrefab).name : null); } obj[3] = (string)obj3; return ContainsPrefabName(recipePreviewPrefabBlacklist, obj); } return false; } private static bool IsPiecePreviewBlacklisted(Piece piece) { HashSet<string> piecePreviewPrefabBlacklist = GetPiecePreviewPrefabBlacklist(); if (piecePreviewPrefabBlacklist.Count > 0) { string[] obj = new string[2] { ((Object)piece).name, null }; GameObject gameObject = ((Component)piece).gameObject; obj[1] = ((gameObject != null) ? ((Object)gameObject).name : null); return ContainsPrefabName(piecePreviewPrefabBlacklist, obj); } return false; } private static HashSet<string> GetRecipePreviewPrefabBlacklist() { return GetPrefabBlacklist(VeiledRecipesPlugin.RecipePreviewPrefabBlacklist, ref _recipePreviewPrefabBlacklistRaw, ref _recipePreviewPrefabBlacklist); } private static HashSet<string> GetPiecePreviewPrefabBlacklist() { return GetPrefabBlacklist(VeiledRecipesPlugin.PiecePreviewPrefabBlacklist, ref _piecePreviewPrefabBlacklistRaw, ref _piecePreviewPrefabBlacklist); } private static bool HasPreviewBlacklistedRequirement(Requirement[] requirements) { HashSet<string> requirementPreviewPrefabBlacklist = GetRequirementPreviewPrefabBlacklist(); if (requirementPreviewPrefabBlacklist.Count == 0 || requirements == null) { return false; } foreach (Requirement val in requirements) { if (!((Object)(object)val?.m_resItem == (Object)null) && val.GetAmount(1) > 0) { string[] obj = new string[3] { ((Object)val.m_resItem).name, null, null }; GameObject gameObject = ((Component)val.m_resItem).gameObject; obj[1] = ((gameObject != null) ? ((Object)gameObject).name : null); GameObject dropPrefab = val.m_resItem.m_itemData.m_dropPrefab; obj[2] = ((dropPrefab != null) ? ((Object)dropPrefab).name : null); if (ContainsPrefabName(requirementPreviewPrefabBlacklist, obj)) { return true; } } } return false; } private static HashSet<string> GetRequirementPreviewPrefabBlacklist() { return GetPrefabBlacklist(VeiledRecipesPlugin.RequirementPreviewPrefabBlacklist, ref _requirementPreviewPrefabBlacklistRaw, ref _requirementPreviewPrefabBlacklist); } private static HashSet<string> GetPrefabBlacklist(ConfigEntry<string> entry, ref string cachedRaw, ref HashSet<string> cached) { string text = entry?.Value ?? ""; if (string.Equals(text, cachedRaw, StringComparison.Ordinal)) { return cached; } cachedRaw = text; cached = new HashSet<string>(StringComparer.OrdinalIgnoreCase); string[] array = text.Split(VeiledRecipeConstants.PrefabBlacklistSeparators, StringSplitOptions.RemoveEmptyEntries); for (int i = 0; i < array.Length; i++) { string text2 = NormalizePrefabName(array[i]); if (!string.IsNullOrEmpty(text2)) { cached.Add(text2); } } return cached; } private static bool ContainsPrefabName(HashSet<string> blacklist, params string?[] names) { for (int i = 0; i < names.Length; i++) { string text = NormalizePrefabName(names[i]); if (!string.IsNullOrEmpty(text) && blacklist.Contains(text)) { return true; } } return false; } private static string NormalizePrefabName(string? name) { string text = name?.Trim() ?? ""; if (text.Length == 0) { return ""; } if (text.EndsWith("(Clone)", StringComparison.OrdinalIgnoreCase)) { text = text.Substring(0, text.Length - "(Clone)".Length).Trim(); } return text; } internal static void RegisterKnownPieceOverride(Func<Piece, bool> predicate) { if (predicate != null && !KnownPieceOverrides.Contains(predicate)) { KnownPieceOverrides.Add(predicate); } } internal static void UnregisterKnownPieceOverride(Func<Piece, bool> predicate) { if (predicate != null) { KnownPieceOverrides.Remove(predicate); } } internal static void RegisterKnownPiecePrefabOverride(string prefabName) { if (!string.IsNullOrWhiteSpace(prefabName)) { KnownPiecePrefabOverrides.Add(prefabName.Trim()); } } internal static void UnregisterKnownPiecePrefabOverride(string prefabName) { if (!string.IsNullOrWhiteSpace(prefabName)) { KnownPiecePrefabOverrides.Remove(prefabName.Trim()); } } internal static void RecordStationInteraction(Player player, CraftingStation station) { if ((Object)(object)player == (Object)null || (Object)(object)station == (Object)null) { return; } if (!EnableStationProximityDiscovery) { player.AddKnownStation(station); } int num = Math.Max(1, station.GetLevel(true)); string key = StationInteractionKey(station.m_name); if (player.m_customData.TryGetValue(key, out var value) && int.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var result) && result >= num) { return; } player.m_customData[key] = num.ToString(CultureInfo.InvariantCulture); try { player.UpdateKnownRecipesList(); } catch (Exception ex) { VeiledRecipesPlugin.PluginLogger.LogDebug((object)("Could not refresh known recipes after station interaction: " + ex.Message)); } } internal static bool IsRecipeActuallyKnown(Player player, Recipe recipe) { if ((Object)(object)player == (Object)null || (Object)(object)recipe == (Object)null || (Object)(object)recipe.m_item == (Object)null) { return false; } if ((Object)(object)ZoneSystem.instance != (Object)null && ZoneSystem.instance.GetGlobalKey((GlobalKeys)23)) { return true; } if (player.m_noPlacementCost || player.NoCostCheat()) { return true; } return player.m_knownRecipes.Contains(recipe.m_item.m_itemData.m_shared.m_name); } internal static bool IsPieceActuallyKnown(Player player, Piece piece) { if ((Object)(object)player == (Object)null || (Object)(object)piece == (Object)null) { return false; } if (piece.m_repairPiece || piece.m_removePiece) { return true; } if ((Object)(object)ZoneSystem.instance != (Object)null && ZoneSystem.instance.GetGlobalKey((GlobalKeys)21)) { return true; } if (player.m_noPlacementCost || player.NoCostCheat()) { return true; } if (HasKnownPieceOverride(piece)) { return true; } return player.m_knownRecipes.Contains(piece.m_name); } internal static bool CanPreviewRecipe(Player player, Recipe recipe) { if (!ShowUnknownCraftingRecipes || (Object)(object)player == (Object)null || (Object)(object)recipe == (Object)null || (Object)(object)recipe.m_item == (Object)null) { return false; } if (IsRecipeActuallyKnown(player, recipe) || !IsRecipeEnabledForPlayer(player, recipe)) { return false; } if (IsRecipePreviewBlacklisted(recipe)) { return false; } if (HasPreviewBlacklistedRequirement(recipe.m_resources)) { return false; } if (!DlcInstalled(recipe.m_item.m_itemData.m_shared.m_dlc)) { return false; } if (!PassesCraftFilter(recipe)) { return false; } bool flag = RequireStationLevelForUnknownCraftingRecipes && (Object)(object)recipe.GetRequiredStation(1) != (Object)null; return player.RequiredCraftingStation(recipe, 1, flag); } internal static bool CanPreviewPiece(Player player, Piece piece) { if (!ShowUnknownBuildPieces || (Object)(object)player == (Object)null || (Object)(object)piece == (Object)null) { return false; } if (IsPieceActuallyKnown(player, piece) || !IsPieceEnabledForPlayer(player, piece)) { return false; } if (IsPiecePreviewBlacklisted(piece)) { return false; } if (HasPreviewBlacklistedRequirement(piece.m_resources)) { return false; } if (RequireStationKnowledgeForUnknownBuildPieces && !KnowsPieceStationRequirement(player, piece)) { return false; } return DlcInstalled(piece.m_dlc); } internal static VeiledRecipeVisibilityState GetRecipeVisibilityState(Player player, Recipe recipe) { if (IsRecipeActuallyKnown(player, recipe)) { return VeiledRecipeVisibilityState.Known; } if (!CanPreviewRecipe(player, recipe)) { return VeiledRecipeVisibilityState.Hidden; } return VeiledRecipeVisibilityState.UnknownPreview; } internal static bool IsUnknownRecipePreview(Player player, Recipe recipe) { return GetRecipeVisibilityState(player, recipe) == VeiledRecipeVisibilityState.UnknownPreview; } internal static bool CanDiscoverRecipe(Player player, Recipe recipe) { if ((Object)(object)player == (Object)null || (Object)(object)recipe == (Object)null || (Object)(object)recipe.m_item == (Object)null) { return false; } if ((Object)(object)recipe.m_craftingStation != (Object)null && !HasKnownRecipeStationLevel(player, recipe.m_craftingStation.m_name, recipe.m_minStationLevel)) { return false; } if (!DlcInstalled(recipe.m_item.m_itemData.m_shared.m_dlc)) { return false; } return HasDiscoveredRecipeMaterials(player, recipe); } internal static bool CanDiscoverPiece(Player player, Piece piece) { if ((Object)(object)player == (Object)null || (Object)(object)piece == (Object)null) { return false; } if ((Object)(object)piece.m_craftingStation != (Object)null && GetKnownPieceStationLevel(player, piece.m_craftingStation.m_name) <= 0) { return false; } if (!DlcInstalled(piece.m_dlc)) { return false; } Requirement[] resources = piece.m_resources; foreach (Requirement val in resources) { if ((Object)(object)val.m_resItem != (Object)null && val.m_amount > 0 && !IsMaterialKnown(player, val)) { return false; } } return true; } internal static bool KnowsRecipeStationRequirement(Player player, Recipe recipe, int quality) { if ((Object)(object)player == (Object)null || (Object)(object)recipe == (Object)null) { return false; } CraftingStation requiredStation = recipe.GetRequiredStation(quality); if (!((Object)(object)requiredStation == (Object)null)) { return HasKnownRecipeStationLevel(player, requiredStation.m_name, recipe.GetRequiredStationLevel(quality)); } return true; } internal static bool KnowsPieceStationRequirement(Player player, Piece piece) { if ((Object)(object)player == (Object)null || (Object)(object)piece == (Object)null || (Object)(object)piece.m_craftingStation == (Object)null) { return true; } return GetKnownPieceStationLevel(player, piece.m_craftingStation.m_name) > 0; } internal static bool IsMaterialKnown(Player player, Requirement requirement) { if ((Object)(object)player == (Object)null || requirement == null || (Object)(object)requirement.m_resItem == (Object)null) { return false; } return player.m_knownMaterial.Contains(requirement.m_resItem.m_itemData.m_shared.m_name); } private static bool HasDiscoveredRecipeMaterials(Player player, Recipe recipe) { bool flag = false; Requirement[] resources = recipe.m_resources; foreach (Requirement val in resources) { if (!((Object)(object)val.m_resItem == (Object)null) && val.m_amount > 0) { bool flag2 = IsMaterialKnown(player, val); if (recipe.m_requireOnlyOneIngredient) { flag = flag || flag2; } else if (!flag2) { return false; } } } return !recipe.m_requireOnlyOneIngredient || flag; } private static int GetKnownRecipeStationLevel(Player player, string stationName) { if ((Object)(object)player == (Object)null || string.IsNullOrEmpty(stationName)) { return 0; } if (RequireStationInteractionForRecipeUnlock) { return GetInteractedStationLevel(player, stationName); } return GetKnownPieceStationLevel(player, stationName); } private static int GetKnownPieceStationLevel(Player player, string stationName) { if ((Object)(object)player == (Object)null || string.IsNullOrEmpty(stationName)) { return 0; } if (!player.m_knownStations.TryGetValue(stationName, out var value)) { return 0; } return value; } private static bool HasKnownRecipeStationLevel(Player player, string stationName, int requiredLevel) { return GetKnownRecipeStationLevel(player, stationName) >= Math.Max(1, requiredLevel); } private static int GetInteractedStationLevel(Player player, string stationName) { if (player.m_customData.TryGetValue(StationInteractionKey(stationName), out var value) && int.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var result)) { return result; } return 0; } private static string StationInteractionKey(string stationName) { return "VeiledRecipes.InteractedStation." + stationName; } private static bool IsRecipeEnabledForPlayer(Player player, Recipe recipe) { bool flag = (Object)(object)player.CurrentSeason != (Object)null && player.CurrentSeason.Recipes.Contains(recipe); return recipe.m_enabled || flag; } private static bool IsPieceEnabledForPlayer(Player player, Piece piece) { bool flag = (Object)(object)player.CurrentSeason != (Object)null && player.CurrentSeason.Pieces.Contains(((Component)piece).gameObject); return piece.m_enabled || flag; } private static bool HasKnownPieceOverride(Piece piece) { if ((Object)(object)piece == (Object)null) { return false; } string prefabName = Utils.GetPrefabName(((Component)piece).gameObject); if (!string.IsNullOrEmpty(prefabName) && KnownPiecePrefabOverrides.Contains(prefabName)) { return true; } foreach (Func<Piece, bool> knownPieceOverride in KnownPieceOverrides) { try { if (knownPieceOverride(piece)) { return true; } } catch (Exception ex) { VeiledRecipesPlugin.PluginLogger.LogDebug((object)("Known piece override failed for '" + prefabName + "': " + ex.Message)); } } return false; } private static bool PassesCraftFilter(Recipe recipe) { if (Player.s_FilterCraft.Count == 0) { return true; } string text = ((Object)recipe.m_item).name.ToLowerInvariant(); string text2 = recipe.m_item.m_itemData.m_shared.m_name.ToLowerInvariant(); string text3 = Localization.instance.Localize(recipe.m_item.m_itemData.m_shared.m_name).ToLowerInvariant(); foreach (string item in Player.s_FilterCraft) { if (!string.IsNullOrWhiteSpace(item)) { string value = item.ToLowerInvariant(); if (text.Contains(value) || text2.Contains(value) || text3.Contains(value)) { return true; } } } return false; } private static bool DlcInstalled(string dlc) { if (!string.IsNullOrEmpty(dlc) && !((Object)(object)DLCMan.instance == (Object)null)) { return DLCMan.instance.IsDLCInstalled(dlc); } return true; } } [HarmonyPatch(typeof(Player), "HaveRequirements", new Type[] { typeof(Piece), typeof(RequirementMode) })] internal static class PlayerPieceRequirementsPatch { private static bool Prefix(Player __instance, Piece piece, RequirementMode mode, ref bool __result) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Invalid comparison between Unknown and I4 if ((Object)(object)piece == (Object)null) { return true; } if ((int)mode != 1 && !VeiledRecipeState.IsPieceActuallyKnown(__instance, piece)) { __result = false; return false; } return true; } } [HarmonyPatch(typeof(PieceTable), "UpdateAvailable")] internal static class PieceTableUpdateAvailablePatch { private static void Postfix(PieceTable __instance, Player player) { if (!VeiledRecipeState.ShowUnknownBuildPieces || (Object)(object)player == (Object)null) { return; } EnsureAvailablePieceBuckets(__instance); HashSet<string> availablePiecePrefabNames = GetAvailablePiecePrefabNames(__instance); foreach (GameObject piece in __instance.m_pieces) { Piece val = (((Object)(object)piece == (Object)null) ? null : piece.GetComponent<Piece>()); string text = (((Object)(object)val == (Object)null) ? "" : Utils.GetPrefabName(((Component)val).gameObject)); if (!((Object)(object)val == (Object)null) && !string.IsNullOrEmpty(text) && !availablePiecePrefabNames.Contains(text) && VeiledRecipeState.CanPreviewPiece(player, val)) { AddAvailablePiece(__instance, val); availablePiecePrefabNames.Add(text); } } } private static void EnsureAvailablePieceBuckets(PieceTable table) { while (table.m_availablePieces.Count < 8) { table.m_availablePieces.Add(new List<Piece>()); } } private static HashSet<string> GetAvailablePiecePrefabNames(PieceTable table) { HashSet<string> hashSet = new HashSet<string>(); foreach (List<Piece> availablePiece in table.m_availablePieces) { foreach (Piece item in availablePiece) { if ((Object)(object)item != (Object)null) { hashSet.Add(Utils.GetPrefabName(((Component)item).gameObject)); } } } return hashSet; } private static void AddAvailablePiece(PieceTable table, Piece piece) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Invalid comparison between Unknown and I4 //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Expected I4, but got Unknown if ((int)piece.m_category == 100) { foreach (List<Piece> availablePiece in table.m_availablePieces) { availablePiece.Add(piece); } return; } int num = (int)piece.m_category; if (num >= 0 && num < table.m_availablePieces.Count) { table.m_availablePieces[num].Add(piece); } } } [HarmonyPatch(typeof(Hud), "UpdatePieceList")] internal static class HudUpdatePieceListPatch { private static void Postfix(Hud __instance, Player player) { //IL_0055: Unknown result type (might be due to invalid IL or missing references) List<Piece> list = ((player != null) ? player.GetBuildPieces() : null); if (list == null) { return; } for (int i = 0; i < __instance.m_pieceIcons.Count && i < list.Count; i++) { Piece val = list[i]; if (!VeiledRecipeState.IsPieceActuallyKnown(player, val)) { PieceIconData obj = __instance.m_pieceIcons[i]; ((Behaviour)obj.m_icon).enabled = true; obj.m_icon.sprite = val.m_icon; ((Graphic)obj.m_icon).color = Color.black; obj.m_tooltip.m_text = VeiledRecipeState.UnknownNameText; obj.m_upgrade.SetActive(false); } } } } [HarmonyPatch(typeof(Hud), "SetupPieceInfo")] internal static class HudSetupPieceInfoPatch { private static void Postfix(Hud __instance, Piece piece) { //IL_002a: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)piece == (Object)null) && !((Object)(object)Player.m_localPlayer == (Object)null)) { if (VeiledRecipeState.IsPieceActuallyKnown(Player.m_localPlayer, piece)) { ((Graphic)__instance.m_buildIcon).color = Color.white; } else { MaskPieceInfo(__instance, Player.m_localPlayer, piece); } } } private static void MaskPieceInfo(Hud hud, Player player, Piece piece) { //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00b7: Invalid comparison between Unknown and I4 hud.m_buildSelection.text = VeiledRecipeState.UnknownNameText; hud.m_pieceDescription.text = VeiledRecipeState.UnknownDescriptionText; ((Behaviour)hud.m_buildIcon).enabled = true; hud.m_buildIcon.sprite = piece.m_icon; ((Graphic)hud.m_buildIcon).color = Color.black; ((Behaviour)hud.m_snappingIcon).enabled = false; int i = 0; Requirement[] resources = piece.m_resources; foreach (Requirement val in resources) { if (i >= hud.m_requirementItems.Length) { break; } if (!((Object)(object)val?.m_resItem == (Object)null) && val.m_amount > 0) { hud.m_requirementItems[i].SetActive(true); InventoryGuiUpdateRecipePatch.SetupRequirement(hud.m_requirementItems[i].transform, val, player, (int)piece.FreeBuildKey() == 20, 0); i++; } } if ((Object)(object)piece.m_craftingStation != (Object)null && i < hud.m_requirementItems.Length) { hud.m_requirementItems[i].SetActive(true); SetupStationRequirement(hud.m_requirementItems[i].transform, player, piece); i++; } for (; i < hud.m_requirementItems.Length; i++) { hud.m_requirementItems[i].SetActive(false); } } private static void SetupStationRequirement(Transform root, Player player, Piece piece) { //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_014d: Unknown result type (might be due to invalid IL or missing references) //IL_00ea: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_0135: Unknown result type (might be due to invalid IL or missing references) //IL_0113: Unknown result type (might be due to invalid IL or missing references) Image val = InventoryGuiUpdateRecipePatch.FindComponent<Image>(root, "res_icon"); TMP_Text val2 = InventoryGuiUpdateRecipePatch.FindComponent<TMP_Text>(root, "res_name"); TMP_Text val3 = InventoryGuiUpdateRecipePatch.FindComponent<TMP_Text>(root, "res_amount"); UITooltip component = ((Component)root).GetComponent<UITooltip>(); bool flag = VeiledRecipeState.KnowsPieceStationRequirement(player, piece); if ((Object)(object)val != (Object)null) { ((Component)val).gameObject.SetActive(true); ((Behaviour)val).enabled = true; val.sprite = piece.m_craftingStation.m_icon; ((Graphic)val).color = (flag ? Color.white : Color.black); } if ((Object)(object)val2 != (Object)null) { ((Component)val2).gameObject.SetActive(true); val2.text = (flag ? Localization.instance.Localize(piece.m_craftingStation.m_name) : VeiledRecipeState.UnknownNameText); ((Graphic)val2).color = Color.white; } if ((Object)(object)val3 != (Object)null) { ((Component)val3).gameObject.SetActive(true); if (flag) { CraftingStation val4 = CraftingStation.HaveBuildStationInRange(piece.m_craftingStation.m_name, ((Component)player).transform.position); if ((Object)(object)val4 != (Object)null) { val4.ShowAreaMarker(); val3.text = ""; ((Graphic)val3).color = Color.white; } else { val3.text = Localization.instance.Localize("$menu_none"); ((Graphic)val3).color = Color.white; } } else { val3.text = VeiledRecipeState.UnknownRequirementText; ((Graphic)val3).color = Color.white; } } if ((Object)(object)component != (Object)null) { component.m_text = (flag ? piece.m_craftingStation.m_name : VeiledRecipeState.UnknownNameText); } } } [HarmonyPatch(typeof(Player), "SetupPlacementGhost")] internal static class PlayerSetupPlacementGhostPatch { private static void Postfix(Player __instance) { Piece selectedPiece = __instance.GetSelectedPiece(); if (!((Object)(object)selectedPiece == (Object)null) && !VeiledRecipeState.IsPieceActuallyKnown(__instance, selectedPiece)) { if ((Object)(object)__instance.m_placementGhost != (Object)null) { Object.Destroy((Object)(object)__instance.m_placementGhost); __instance.m_placementGhost = null; } if ((Object)(object)__instance.m_placementMarkerInstance != (Object)null) { __instance.m_placementMarkerInstance.SetActive(false); } } } } [HarmonyPatch(typeof(Player), "TryPlacePiece")] internal static class PlayerTryPlacePiecePatch { private static bool Prefix(Player __instance, Piece piece, ref bool __result) { if ((Object)(object)piece != (Object)null && !VeiledRecipeState.IsPieceActuallyKnown(__instance, piece)) { ((Character)__instance).Message((MessageType)2, "$msg_missingrequirement", 0, (Sprite)null); __result = false; return false; } return true; } } [HarmonyPatch(typeof(Player), "HaveRequirements", new Type[] { typeof(Recipe), typeof(bool), typeof(int), typeof(int) })] internal static class PlayerRecipeRequirementsPatch { private static bool Prefix(Player __instance, Recipe recipe, bool discover, ref bool __result) { if ((Object)(object)recipe == (Object)null) { return true; } if (discover && VeiledRecipeState.RequireStationInteractionForRecipeUnlock) { __result = VeiledRecipeState.CanDiscoverRecipe(__instance, recipe); return false; } if (!discover && !VeiledRecipeState.IsRecipeActuallyKnown(__instance, recipe)) { __result = false; return false; } return true; } } [HarmonyPatch(typeof(Player), "GetAvailableRecipes")] internal static class PlayerGetAvailableRecipesPatch { private static void Postfix(Player __instance, ref List<Recipe> available) { if (!VeiledRecipeState.ShowUnknownCraftingRecipes || (Object)(object)ObjectDB.instance == (Object)null) { return; } HashSet<Recipe> hashSet = new HashSet<Recipe>(available); foreach (Recipe recipe in ObjectDB.instance.m_recipes) { if (!hashSet.Contains(recipe) && VeiledRecipeState.CanPreviewRecipe(__instance, recipe)) { available.Add(recipe); hashSet.Add(recipe); } } } } [HarmonyPatch(typeof(InventoryGui), "AddRecipeToList")] internal static class InventoryGuiAddRecipeToListPatch { private static void Prefix(Player player, Recipe recipe, ref bool canCraft) { if (!VeiledRecipeState.IsRecipeActuallyKnown(player, recipe)) { canCraft = false; } } private static void Postfix(InventoryGui __instance, Player player, Recipe recipe, ItemData item) { if (!VeiledRecipeState.IsRecipeActuallyKnown(player, recipe)) { GameObject val = FindRecipeElement(__instance, recipe, item); if ((Object)(object)val != (Object)null) { MaskRecipeListElement(val, recipe); } } } private static GameObject FindRecipeElement(InventoryGui gui, Recipe recipe, ItemData item) { //IL_0017: 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) for (int num = gui.m_availableRecipes.Count - 1; num >= 0; num--) { RecipeDataPair val = gui.m_availableRecipes[num]; if ((Object)(object)((RecipeDataPair)(ref val)).Recipe == (Object)(object)recipe && ((RecipeDataPair)(ref val)).ItemData == item) { return ((RecipeDataPair)(ref val)).InterfaceElement; } } return null; } private static void MaskRecipeListElement(GameObject element, Recipe recipe) { //IL_007c: 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) Image val = FindComponent<Image>(element.transform, "icon"); if ((Object)(object)val != (Object)null) { ((Behaviour)val).enabled = true; if ((Object)(object)recipe?.m_item != (Object)null) { val.sprite = recipe.m_item.m_itemData.GetIcon(); } ((Graphic)val).color = Color.black; } TMP_Text val2 = FindComponent<TMP_Text>(element.transform, "name"); if ((Object)(object)val2 != (Object)null) { val2.text = VeiledRecipeState.UnknownNameText; ((Graphic)val2).color = Color.white; } GuiBar val3 = FindComponent<GuiBar>(element.transform, "Durability"); if ((Object)(object)val3 != (Object)null) { ((Component)val3).gameObject.SetActive(false); } TMP_Text val4 = FindComponent<TMP_Text>(element.transform, "QualityLevel"); if ((Object)(object)val4 != (Object)null) { ((Component)val4).gameObject.SetActive(false); } } private static T FindComponent<T>(Transform root, string childName) where T : Component { Transform val = root.Find(childName); if (!((Object)(object)val == (Object)null)) { return ((Component)val).GetComponent<T>(); } return default(T); } } [HarmonyPatch(typeof(InventoryGui), "UpdateRecipeList", new Type[] { typeof(List<Recipe>) })] internal static class InventoryGuiUpdateRecipeListPatch { private static void Postfix(InventoryGui __instance) { //IL_0041: 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_0062: 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_00c3: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_00f3: Unknown result type (might be due to invalid IL or missing references) Player localPlayer = Player.m_localPlayer; if (!VeiledRecipeState.GroupUnknownRecipePreviewsBelowKnownRecipes || (Object)(object)localPlayer == (Object)null || __instance.m_availableRecipes.Count <= 1) { return; } List<RecipeDataPair> list = new List<RecipeDataPair>(); List<RecipeDataPair> list2 = new List<RecipeDataPair>(); foreach (RecipeDataPair availableRecipe in __instance.m_availableRecipes) { RecipeDataPair current = availableRecipe; if (VeiledRecipeState.IsUnknownRecipePreview(localPlayer, ((RecipeDataPair)(ref current)).Recipe)) { list2.Add(current); } else { list.Add(current); } } if (list.Count == 0 || list2.Count == 0) { return; } __instance.m_availableRecipes.Clear(); __instance.m_availableRecipes.AddRange(list); __instance.m_availableRecipes.AddRange(list2); for (int i = 0; i < __instance.m_availableRecipes.Count; i++) { RecipeDataPair val = __instance.m_availableRecipes[i]; Transform transform = ((RecipeDataPair)(ref val)).InterfaceElement.transform; RectTransform val2 = (RectTransform)(object)((transform is RectTransform) ? transform : null); if (val2 != null) { val2.anchoredPosition = new Vector2(0f, (float)i * (0f - __instance.m_recipeListSpace)); } } } } [HarmonyPatch(typeof(InventoryGui), "UpdateRecipe")] internal static class InventoryGuiUpdateRecipePatch { private static void Postfix(InventoryGui __instance, Player player) { //IL_0025: Unknown result type (might be due to invalid IL or missing references) Recipe recipe = ((RecipeDataPair)(ref __instance.m_selectedRecipe)).Recipe; if (!((Object)(object)recipe == (Object)null)) { if (VeiledRecipeState.IsRecipeActuallyKnown(player, recipe)) { ((Graphic)__instance.m_recipeIcon).color = Color.white; } else { MaskSelectedRecipe(__instance, player, recipe); } } } private static void MaskSelectedRecipe(InventoryGui gui, Player player, Recipe recipe) { //IL_00ae: Unknown result type (might be due to invalid IL or missing references) ItemData itemData = ((RecipeDataPair)(ref gui.m_selectedRecipe)).ItemData; int num = ((itemData == null) ? 1 : (itemData.m_quality + 1)); int craftMultiplier = ((itemData != null || (!ZInput.GetButton("AltPlace") && !ZInput.GetButton("JoyLStick"))) ? 1 : gui.m_multiCraftAmount); bool allowedQuality = (Object)(object)recipe.m_item != (Object)null && num <= recipe.m_item.m_itemData.m_shared.m_maxQuality; ((Behaviour)gui.m_recipeIcon).enabled = true; if ((Object)(object)recipe.m_item != (Object)null) { gui.m_recipeIcon.sprite = recipe.m_item.m_itemData.GetIcon(); } ((Graphic)gui.m_recipeIcon).color = Color.black; ((Behaviour)gui.m_recipeName).enabled = true; gui.m_recipeName.text = VeiledRecipeState.UnknownNameText; ((Behaviour)gui.m_recipeDecription).enabled = true; gui.m_recipeDecription.text = VeiledRecipeState.UnknownDescriptionText; ((Component)gui.m_variantButton).gameObject.SetActive(false); ((Component)gui.m_itemCraftType).gameObject.SetActive(false); ((Component)gui.m_qualityPanel).gameObject.SetActive(false); ((Selectable)gui.m_craftButton).interactable = false; ((Component)gui.m_craftButton).GetComponent<UITooltip>().m_text = VeiledRecipeState.UnknownDescriptionText; SetupRecipeRequirements(gui, player, recipe, num, allowedQuality, craftMultiplier); SetupRecipeStationLevel(gui, player, recipe, num, allowedQuality); } private static void SetupRecipeStationLevel(InventoryGui gui, Player player, Recipe recipe, int quality, bool allowedQuality) { //IL_0068: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)recipe.GetRequiredStation(quality) == (Object)null || !allowedQuality) { ((Component)gui.m_minStationLevelIcon).gameObject.SetActive(false); return; } ((Component)gui.m_minStationLevelIcon).gameObject.SetActive(true); gui.m_minStationLevelText.text = (VeiledRecipeState.KnowsRecipeStationRequirement(player, recipe, quality) ? recipe.GetRequiredStationLevel(quality).ToString() : VeiledRecipeState.UnknownRequirementText); ((Graphic)gui.m_minStationLevelText).color = gui.m_minStationLevelBasecolor; } private static void SetupRecipeRequirements(InventoryGui gui, Player player, Recipe recipe, int quality, bool allowedQuality, int craftMultiplier) { int i = 0; List<Requirement> visibleRequirements = GetVisibleRequirements(recipe.m_resources, quality); int cyclingStart = GetCyclingStart(visibleRequirements.Count, gui.m_recipeRequirementList.Length); if (allowedQuality) { for (int j = cyclingStart; j < visibleRequirements.Count; j++) { if (i >= gui.m_recipeRequirementList.Length) { break; } SetupRequirement(gui.m_recipeRequirementList[i].transform, visibleRequirements[j], player, craft: true, quality, craftMultiplier); i++; } } for (; i < gui.m_recipeRequirementList.Length; i++) { InventoryGui.HideRequirement(gui.m_recipeRequirementList[i].transform); } } internal static void SetupRequirement(Transform root, Requirement requirement, Player player, bool craft, int quality, int multiplier = 1) { if (requirement == null || (Object)(object)requirement.m_resItem == (Object)null || requirement.GetAmount(quality) <= 0) { InventoryGui.HideRequirement(root); } else if (VeiledRecipeState.IsMaterialKnown(player, requirement)) { InventoryGui.SetupRequirement(root, requirement, player, craft, quality, multiplier); } else { SetupMaskedRequirement(root, requirement); } } internal static void SetupMaskedRequirement(Transform root, Requirement requirement) { //IL_009d: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) Image val = InventoryGuiUpdateRecipePatch.FindComponent<Image>(root, "res_icon"); TMP_Text val2 = InventoryGuiUpdateRecipePatch.FindComponent<TMP_Text>(root, "res_name"); TMP_Text val3 = InventoryGuiUpdateRecipePatch.FindComponent<TMP_Text>(root, "res_amount"); UITooltip component = ((Component)root).GetComponent<UITooltip>(); if ((Object)(object)val != (Object)null) { ((Component)val).gameObject.SetActive(true); ((Behaviour)val).enabled = true; if ((Object)(object)requirement?.m_resItem != (Object)null) { val.sprite = requirement.m_resItem.m_itemData.GetIcon(); } ((Graphic)val).color = Color.black; } if ((Object)(object)val2 != (Object)null) { ((Component)val2).gameObject.SetActive(true); val2.text = VeiledRecipeState.UnknownNameText; ((Graphic)val2).color = Color.white; } if ((Object)(object)val3 != (Object)null) { ((Component)val3).gameObject.SetActive(true); val3.text = VeiledRecipeState.UnknownRequirementText; ((Graphic)val3).color = Color.white; } if ((Object)(object)component != (Object)null) { component.m_text = VeiledRecipeState.UnknownNameText; } } internal static T FindComponent<T>(Transform root, string childName) where T : Component { Transform val = root.Find(childName); if (!((Object)(object)val == (Object)null)) { return ((Component)val).GetComponent<T>(); } return default(T); } private static List<Requirement> GetVisibleRequirements(Requirement[] source, int quality) { List<Requirement> list = new List<Requirement>(); foreach (Requirement val in source) { if ((Object)(object)val?.m_resItem != (Object)null && val.GetAmount(quality) > 0) { list.Add(val); } } return list; } private static int GetCyclingStart(int requirementCount, int slotCount) { if (slotCount <= 0 || requirementCount <= slotCount) { return 0; } int num = Mathf.CeilToInt((float)requirementCount / (float)slotCount); return (int)Time.fixedTime % num * slotCount; } } [HarmonyPatch(typeof(InventoryGui), "OnCraftPressed")] internal static class InventoryGuiOnCraftPressedPatch { private static bool Prefix(InventoryGui __instance) { Recipe recipe = ((RecipeDataPair)(ref __instance.m_selectedRecipe)).Recipe; if ((Object)(object)recipe != (Object)null && !VeiledRecipeState.IsRecipeActuallyKnown(Player.m_localPlayer, recipe)) { ((Character)Player.m_localPlayer).Message((MessageType)2, "$msg_missingrequirement", 0, (Sprite)null); return false; } return true; } } [HarmonyPatch(typeof(InventoryGui), "DoCrafting")] internal static class InventoryGuiDoCraftingPatch { private static bool Prefix(InventoryGui __instance, Player player) { Recipe craftRecipe = __instance.m_craftRecipe; if ((Object)(object)craftRecipe != (Object)null && !VeiledRecipeState.IsRecipeActuallyKnown(player, craftRecipe)) { __instance.m_craftTimer = -1f; ((Character)player).Message((MessageType)2, "$msg_missingrequirement", 0, (Sprite)null); return false; } return true; } } [HarmonyPatch(typeof(CraftingStation), "Interact")] internal static class CraftingStationInteractPatch { private static void Prefix(CraftingStation __instance, Humanoid user, bool repeat) { if (!repeat && !((Object)(object)user != (Object)(object)Player.m_localPlayer)) { Player val = (Player)(object)((user is Player) ? user : null); if (val != null && __instance.InUseDistance(user) && __instance.CheckUsable(val, false)) { VeiledRecipeState.RecordStationInteraction(val, __instance); } } } } [HarmonyPatch(typeof(CraftingStation), "UpdateKnownStationsInRange")] internal static class CraftingStationUpdateKnownStationsInRangePatch { private static bool Prefix() { return VeiledRecipeState.EnableStationProximityDiscovery; } } public enum VeiledRecipeVisibilityState { Hidden, Known, UnknownPreview } public static class VeiledRecipesCompat { public const string PluginGuid = "sighsorry.VeiledRecipes"; public const string PluginName = "VeiledRecipes"; public const string PluginVersion = "1.0.1"; public const string Author = "sighsorry"; public static string UnknownNameText => VeiledRecipeState.UnknownNameText; public static string UnknownDescriptionText => VeiledRecipeState.UnknownDescriptionText; public static string UnknownRequirementText => VeiledRecipeState.UnknownRequirementText; public static bool GroupUnknownRecipePreviewsBelowKnownRecipes => VeiledRecipeState.GroupUnknownRecipePreviewsBelowKnownRecipes; public static VeiledRecipeVisibilityState GetRecipeVisibilityState(Recipe recipe) { return GetRecipeVisibilityState(Player.m_localPlayer, recipe); } public static VeiledRecipeVisibilityState GetRecipeVisibilityState(Player player, Recipe recipe) { if (!((Object)(object)player != (Object)null) || !((Object)(object)recipe != (Object)null)) { return VeiledRecipeVisibilityState.Hidden; } return VeiledRecipeState.GetRecipeVisibilityState(player, recipe); } public static bool IsUnknownRecipePreview(Recipe recipe) { return IsUnknownRecipePreview(Player.m_localPlayer, recipe); } public static bool IsUnknownRecipePreview(Player player, Recipe recipe) { if ((Object)(object)player != (Object)null && (Object)(object)recipe != (Object)null) { return VeiledRecipeState.IsUnknownRecipePreview(player, recipe); } return false; } public static bool ShouldMaskRecipe(Recipe recipe) { return ShouldMaskRecipe(Player.m_localPlayer, recipe); } public static bool ShouldMaskRecipe(Player player, Recipe recipe) { if ((Object)(object)player != (Object)null && (Object)(object)recipe != (Object)null) { return !VeiledRecipeState.IsRecipeActuallyKnown(player, recipe); } return false; } public static bool ShouldMaskRecipePair(RecipeDataPair pair) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) return ShouldMaskRecipePair(Player.m_localPlayer, pair); } public static bool ShouldMaskRecipePair(Player player, RecipeDataPair pair) { return ShouldMaskRecipe(player, ((RecipeDataPair)(ref pair)).Recipe); } public static bool IsRecipeActuallyKnown(Player player, Recipe recipe) { if ((Object)(object)player != (Object)null && (Object)(object)recipe != (Object)null) { return VeiledRecipeState.IsRecipeActuallyKnown(player, recipe); } return false; } public static bool ShouldMaskPiece(Piece piece) { return ShouldMaskPiece(Player.m_localPlayer, piece); } public static bool ShouldMaskPiece(Player player, Piece piece) { if ((Object)(object)player != (Object)null && (Object)(object)piece != (Object)null) { return !VeiledRecipeState.IsPieceActuallyKnown(player, piece); } return false; } public static void RegisterKnownPieceOverride(Func<Piece, bool> predicate) { VeiledRecipeState.RegisterKnownPieceOverride(predicate); } public static void UnregisterKnownPieceOverride(Func<Piece, bool> predicate) { VeiledRecipeState.UnregisterKnownPieceOverride(predicate); } public static void RegisterKnownPiecePrefabOverride(string prefabName) { VeiledRecipeState.RegisterKnownPiecePrefabOverride(prefabName); } public static void UnregisterKnownPiecePrefabOverride(string prefabName) { VeiledRecipeState.UnregisterKnownPiecePrefabOverride(prefabName); } public static bool IsPieceActuallyKnown(Player player, Piece piece) { if ((Object)(object)player != (Object)null && (Object)(object)piece != (Object)null) { return VeiledRecipeState.IsPieceActuallyKnown(player, piece); } return false; } public static bool IsMaterialKnown(Requirement requirement) { return IsMaterialKnown(Player.m_localPlayer, requirement); } public static bool IsMaterialKnown(Player player, Requirement requirement) { if ((Object)(object)player != (Object)null && requirement != null) { return VeiledRecipeState.IsMaterialKnown(player, requirement); } return false; } public static bool KnowsRecipeStationRequirement(Recipe recipe, int quality) { return KnowsRecipeStationRequirement(Player.m_localPlayer, recipe, quality); } public static bool KnowsRecipeStationRequirement(Player player, Recipe recipe, int quality) { if ((Object)(object)player != (Object)null && (Object)(object)recipe != (Object)null) { return VeiledRecipeState.KnowsRecipeStationRequirement(player, recipe, quality); } return false; } public static bool KnowsPieceStationRequirement(Piece piece) { return KnowsPieceStationRequirement(Player.m_localPlayer, piece); } public static bool KnowsPieceStationRequirement(Player player, Piece piece) { if ((Object)(object)player != (Object)null && (Object)(object)piece != (Object)null) { return VeiledRecipeState.KnowsPieceStationRequirement(player, piece); } return false; } } [HarmonyPatch(typeof(ZNet), "OnNewConnection")] public static class RegisterAndCheckVersion { private static void Prefix(ZNetPeer peer, ref ZNet __instance) { //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Expected O, but got Unknown VeiledRecipesPlugin.PluginLogger.LogDebug((object)"Registering version RPC handler"); peer.m_rpc.Register<ZPackage>("VeiledRecipes_VersionCheck", (Action<ZRpc, ZPackage>)RpcHandlers.RPC_VeiledRecipes_Version); VeiledRecipesPlugin.PluginLogger.LogInfo((object)"Invoking version check"); ZPackage val = new ZPackage(); val.Write("1.0.1"); peer.m_rpc.Invoke("VeiledRecipes_VersionCheck", new object[1] { val }); } } [HarmonyPatch(typeof(ZNet), "RPC_PeerInfo")] public static class VerifyClient { private static bool Prefix(ZRpc rpc, ZPackage pkg, ref ZNet __instance) { if (!__instance.IsServer() || RpcHandlers.ValidatedPeers.Contains(rpc)) { return true; } VeiledRecipesPlugin.PluginLogger.LogWarning((object)("Peer (" + rpc.m_socket.GetHostName() + ") never sent version or couldn't due to previous disconnect, disconnecting")); rpc.Invoke("Error", new object[1] { 3 }); return false; } private static void Postfix(ZNet __instance) { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Expected O, but got Unknown ZRoutedRpc.instance.InvokeRoutedRPC(ZRoutedRpc.instance.GetServerPeerID(), "VeiledRecipesRequestAdminSync", new object[1] { (object)new ZPackage() }); } } [HarmonyPatch(typeof(FejdStartup), "ShowConnectError")] public class ShowConnectionError { private static void Postfix(FejdStartup __instance) { if (__instance.m_connectionFailedPanel.activeSelf) { __instance.m_connectionFailedError.fontSizeMax = 25f; __instance.m_connectionFailedError.fontSizeMin = 15f; TMP_Text connectionFailedError = __instance.m_connectionFailedError; connectionFailedError.text = connectionFailedError.text + "\n" + VeiledRecipesPlugin.ConnectionError; } } } [HarmonyPatch(typeof(ZNet), "Disconnect")] public static class RemoveDisconnectedPeerFromVerified { private static void Prefix(ZNetPeer peer, ref ZNet __instance) { if (__instance.IsServer()) { VeiledRecipesPlugin.PluginLogger.LogInfo((object)("Peer (" + peer.m_rpc.m_socket.GetHostName() + ") disconnected, removing from validated list")); RpcHandlers.ValidatedPeers.Remove(peer.m_rpc); } } } public static class RpcHandlers { public static readonly List<ZRpc> ValidatedPeers = new List<ZRpc>(); public static void RPC_VeiledRecipes_Version(ZRpc rpc, ZPackage pkg) { string text = pkg.ReadString(); VeiledRecipesPlugin.PluginLogger.LogInfo((object)("Version check, local: 1.0.1, remote: " + text)); if (text != "1.0.1") { VeiledRecipesPlugin.ConnectionError = "VeiledRecipes Installed: 1.0.1\n Needed: " + text; if (ZNet.instance.IsServer()) { VeiledRecipesPlugin.PluginLogger.LogWarning((object)("Peer (" + rpc.m_socket.GetHostName() + ") has incompatible version, disconnecting...")); rpc.Invoke("Error", new object[1] { 3 }); } } else if (!ZNet.instance.IsServer()) { VeiledRecipesPlugin.PluginLogger.LogInfo((object)"Received same version from server!"); } else { VeiledRecipesPlugin.PluginLogger.LogInfo((object)("Adding peer (" + rpc.m_socket.GetHostName() + ") to validated list")); ValidatedPeers.Add(rpc); } } } } 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; base..ctor(); } 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 : ZPlayFabSocket, 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; ((ZPlayFabSocket)this)..ctor(); } 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()) { return; } BufferingSocket bufferingSocket = new BufferingSocket(rpc.GetSocket()); AccessTools.DeclaredField(typeof(ZRpc), "m_socket").SetValue(rpc, bufferingSocket); 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) { FieldInfo fieldInfo = AccessTools.DeclaredField(typeof(ZNetPeer), "m_socket"); object? value = fieldInfo.GetValue(val); ZPlayFabSocket val2 = (ZPlayFabSocket)((value is ZPlayFabSocket) ? value : null); if (val2 != null) { typeof(ZPlayFabSocket).GetField("m_remotePlayerId").SetValue(bufferingSocket, val2.m_remotePlayerId); } fieldInfo.SetValue(val, bufferingSocket); } if (__state == null) { __state = new Dictionary<Assembly, BufferingSocket>(); } __state[Assembly.GetExecutingAssembly()] = bufferingSocket; } [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; val2.Save(); } 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 == "serverversion") { if (obj?.ToString() != CurrentVersion) { Debug.LogWarning((object)("Received server version is not equal: server version = " + (obj?.ToString() ?? "null") + "; local version = " + (CurrentVersion ?? "unknown"))); } } else if (text2 == "lockexempt") { if (obj is bool flag) { lockExempt = flag; } } else if (dictionary2.TryGetValue(text2, out value)) { if ((text3 == "" && (!value.Type.IsValueType || Nullable.GetUnderlyingType(value.Type) != null)) || GetZPackageTypeString(value.Type) == text3) { parsedConfigs.customValues[value] = obj; continue; } Debug.LogWarning((object)("Got unexpected type " + text3 + " for internal value " + text2 + " for mod " + (DisplayName ?? Name) + ", expecting " + value.Type.AssemblyQualifiedName)); } } else if (dictionary.TryGetValue(text + "_" + text2, out value2)) { Type type2 = configType(value2.BaseConfig); if ((text3 == "" && (!type2.IsValueType || Nullable.GetUnderlyingType(type2) != null)) || GetZPackageTypeString(type2) == text3) { parsedConfigs.configValues[value2] = obj; continue; } Debug.LogWarning((object)("Got unexpected type " + text3 + " for " + text2 + " in section " + text + " for mod " + (DisplayName ?? Name) + ", expecting " + type2.AssemblyQualifiedName)); } else { Debug.LogWarning((object)("Received unknown config entry " + text2 + " in section " + text + " for mod " + (DisplayName ?? Name) + ". This may happen if client and server versions of the mod do not match.")); } continue; } Debug.LogWarning((object)("Got invalid type " + text3 + ", abort reading of received configs")); return new ParsedConfigs(); } return parsedConfigs; } private static bool isWritableConfig(OwnConfigEntryBase config) { OwnConfigEntryBase config2 = config; ConfigSync configSync = configSyncs.FirstOrDefault((ConfigSync cs) => cs.allConfigs.Contains(config2)); if (configSync == null) { return true; } return configSync.IsSourceOfTruth || !config2.SynchronizedConfig || config2.LocalBaseValue == null || (!configSync.IsLocked && (config2 != configSync.lockedConfig || lockExempt)); } private void serverLockedSettingChanged() { foreach (OwnConfigEntryBase allConfig in allConfigs) { configAttribute<ConfigurationManagerAttributes>(allConfig.BaseConfig).ReadOnly = !isWritableConfig(allConfig); } } private void resetConfigsFromServer() { ConfigFile val = null; bool saveOnConfigSet = false; foreach (OwnConfigEntryBase item in allConfigs.Where((OwnConfigEntryBase config) => config.LocalBaseValue != null)) { if (val == null) { val = item.BaseConfig.ConfigFile; saveOnConfigSet = val.SaveOnConfigSet; val.SaveOnConfigSet = false; } item.BaseConfig.BoxedValue = item.LocalBaseValue; item.LocalBaseValue = null; } if (val != null) { val.SaveOnConfigSet = saveOnConfigSet; } foreach (CustomSyncedValueBase item2 in allCustomValues.Where((CustomSyncedValueBase config) => config.LocalBaseValue != null)) { item2.BoxedValue = item2.LocalBaseValue; item2.LocalBaseValue = null; } lockedConfigChanged -= serverLockedSettingChanged; serverLockedSettingChanged(); } private IEnumerator<bool> distributeConfigToPeers(ZNetPeer peer, ZPackage package) { ZNetPeer peer2 = peer; ZRoutedRpc rpc = ZRoutedRpc.instance; if (rpc == null) { yield break; } byte[] data = package.GetArray(); if (data != null && data.LongLength > 250000) { int fragments = (int)(1 + (data.LongLength - 1) / 250000); long packageIdentifier = ++packageCounter; int fragment = 0; while (fragment < fragments) { foreach (bool item in waitForQueue()) { yield return item; } if (peer2.m_socket.IsConnected()) { ZPackage fragmentedPackage = new ZPackage(); fragmentedPackage.Write((byte)2); fragmentedPackage.Write(packageIdentifier); fragmentedPackage.Write(fragment); fragmentedPackage.Write(fragments); fragmentedPackage.Write(data.Skip(250000 * fragment).Take(250000).ToArray()); SendPackage(fragmentedPackage); if (fragment != fragments - 1) { yield return true; } int num = fragment + 1; fragment = num; continue; } break; } yield break; } foreach (bool item2 in waitForQueue()) { yield return item2; } SendPackage(package); void SendPackage(ZPackage pkg) { string text = Name + " ConfigSync"; if (isServer) { peer2.m_rpc.Invoke(text, new object[1] { pkg }); } else { rpc.Inv