using System; using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.IO; using System.IO.Compression; using System.Linq; using System.Linq.Expressions; 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 System.Text; using System.Text.RegularExpressions; using System.Threading; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using JetBrains.Annotations; using LocalizationManager; using Microsoft.CodeAnalysis; using ServerSync; using TMPro; using UnityEngine; using UnityEngine.UI; using YamlDotNet.Core; using YamlDotNet.Core.Events; using YamlDotNet.Core.ObjectPool; using YamlDotNet.Core.Tokens; using YamlDotNet.Helpers; using YamlDotNet.RepresentationModel; using YamlDotNet.Serialization; using YamlDotNet.Serialization.BufferedDeserialization; using YamlDotNet.Serialization.BufferedDeserialization.TypeDiscriminators; using YamlDotNet.Serialization.Callbacks; using YamlDotNet.Serialization.Converters; using YamlDotNet.Serialization.EventEmitters; using YamlDotNet.Serialization.NamingConventions; using YamlDotNet.Serialization.NodeDeserializers; using YamlDotNet.Serialization.NodeTypeResolvers; using YamlDotNet.Serialization.ObjectFactories; using YamlDotNet.Serialization.ObjectGraphTraversalStrategies; using YamlDotNet.Serialization.ObjectGraphVisitors; using YamlDotNet.Serialization.Schemas; using YamlDotNet.Serialization.TypeInspectors; using YamlDotNet.Serialization.TypeResolvers; using YamlDotNet.Serialization.Utilities; using YamlDotNet.Serialization.ValueDeserializers; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyTitle("FeedLikeGrandma")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("sighsorry")] [assembly: AssemblyProduct("FeedLikeGrandma")] [assembly: AssemblyCopyright("Copyright © 2026")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("4358610B-F3F4-4843-B7AF-98B7BC60DCDE")] [assembly: AssemblyFileVersion("1.0.0")] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace LocalizationManager { [PublicAPI] public class Localizer { private static readonly Dictionary>> PlaceholderProcessors; private static readonly Dictionary> loadedTexts; private static readonly ConditionalWeakTable localizationLanguage; private static readonly List> localizationObjects; private static BaseUnityPlugin? _plugin; private static readonly List fileExtensions; private static BaseUnityPlugin plugin { get { //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Expected O, but got Unknown if (_plugin == null) { IEnumerable source; try { source = Assembly.GetExecutingAssembly().DefinedTypes.ToList(); } catch (ReflectionTypeLoadException ex) { source = from t in ex.Types where t != null select t.GetTypeInfo(); } _plugin = (BaseUnityPlugin)Chainloader.ManagerObject.GetComponent((Type)source.First((TypeInfo t) => t.IsClass && typeof(BaseUnityPlugin).IsAssignableFrom(t))); } return _plugin; } } public static event Action? OnLocalizationComplete; private static void UpdatePlaceholderText(Localization localization, string key) { localizationLanguage.TryGetValue(localization, out string value); string text = loadedTexts[value][key]; if (PlaceholderProcessors.TryGetValue(key, out Dictionary> value2)) { text = value2.Aggregate(text, (string current, KeyValuePair> kv) => current.Replace("{" + kv.Key + "}", kv.Value())); } localization.AddWord(key, text); } public static void AddPlaceholder(string key, string placeholder, ConfigEntry config, Func? convertConfigValue = null) where T : notnull { if (convertConfigValue == null) { convertConfigValue = (T val) => val.ToString(); } if (!PlaceholderProcessors.ContainsKey(key)) { PlaceholderProcessors[key] = new Dictionary>(); } config.SettingChanged += delegate { UpdatePlaceholder(); }; if (loadedTexts.ContainsKey(Localization.instance.GetSelectedLanguage())) { UpdatePlaceholder(); } void UpdatePlaceholder() { PlaceholderProcessors[key][placeholder] = () => convertConfigValue(config.Value); UpdatePlaceholderText(Localization.instance, key); } } public static void AddText(string key, string text) { List> list = new List>(); foreach (WeakReference localizationObject in localizationObjects) { if (localizationObject.TryGetTarget(out var target)) { Dictionary dictionary = loadedTexts[localizationLanguage.GetOrCreateValue(target)]; if (!target.m_translations.ContainsKey(key)) { dictionary[key] = text; target.AddWord(key, text); } } else { list.Add(localizationObject); } } foreach (WeakReference item in list) { localizationObjects.Remove(item); } } public static void Load() { _ = plugin; } public static void LoadLocalizationLater(Localization __instance) { LoadLocalization(Localization.instance, __instance.GetSelectedLanguage()); } public static void SafeCallLocalizeComplete() { Localizer.OnLocalizationComplete?.Invoke(); } private static void LoadLocalization(Localization __instance, string language) { if (!localizationLanguage.Remove(__instance)) { localizationObjects.Add(new WeakReference(__instance)); } localizationLanguage.Add(__instance, language); Dictionary dictionary = new Dictionary(); foreach (string item in from f in Directory.GetFiles(Path.GetDirectoryName(Paths.PluginPath), plugin.Info.Metadata.Name + ".*", SearchOption.AllDirectories) where fileExtensions.IndexOf(Path.GetExtension(f)) >= 0 select f) { string[] array = Path.GetFileNameWithoutExtension(item).Split(new char[1] { '.' }); if (array.Length >= 2) { string text = array[1]; if (dictionary.ContainsKey(text)) { Debug.LogWarning((object)("Duplicate key " + text + " found for " + plugin.Info.Metadata.Name + ". The duplicate file found at " + item + " will be skipped.")); } else { dictionary[text] = item; } } } byte[] array2 = LoadTranslationFromAssembly("English"); if (array2 == null) { throw new Exception("Found no English localizations in mod " + plugin.Info.Metadata.Name + ". Expected an embedded resource translations/English.json or translations/English.yml."); } Dictionary dictionary2 = new DeserializerBuilder().IgnoreFields().Build().Deserialize>(Encoding.UTF8.GetString(array2)); if (dictionary2 == null) { throw new Exception("Localization for mod " + plugin.Info.Metadata.Name + " failed: Localization file was empty."); } string text2 = null; if (language != "English") { if (dictionary.TryGetValue(language, out var value)) { text2 = File.ReadAllText(value); } else { byte[] array3 = LoadTranslationFromAssembly(language); if (array3 != null) { text2 = Encoding.UTF8.GetString(array3); } } } if (text2 == null && dictionary.TryGetValue("English", out var value2)) { text2 = File.ReadAllText(value2); } if (text2 != null) { foreach (KeyValuePair item2 in new DeserializerBuilder().IgnoreFields().Build().Deserialize>(text2) ?? new Dictionary()) { dictionary2[item2.Key] = item2.Value; } } loadedTexts[language] = dictionary2; foreach (KeyValuePair item3 in dictionary2) { UpdatePlaceholderText(__instance, item3.Key); } } static Localizer() { //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Expected O, but got Unknown //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_00ca: Expected O, but got Unknown //IL_00f8: Unknown result type (might be due to invalid IL or missing references) //IL_0105: Expected O, but got Unknown PlaceholderProcessors = new Dictionary>>(); loadedTexts = new Dictionary>(); localizationLanguage = new ConditionalWeakTable(); localizationObjects = new List>(); fileExtensions = new List(2) { ".json", ".yml" }; Harmony val = new Harmony("org.bepinex.helpers.LocalizationManager"); val.Patch((MethodBase)AccessTools.DeclaredMethod(typeof(Localization), "SetupLanguage", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(AccessTools.DeclaredMethod(typeof(Localizer), "LoadLocalization", (Type[])null, (Type[])null)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); val.Patch((MethodBase)AccessTools.DeclaredMethod(typeof(FejdStartup), "SetupGui", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(AccessTools.DeclaredMethod(typeof(Localizer), "LoadLocalizationLater", (Type[])null, (Type[])null)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); val.Patch((MethodBase)AccessTools.DeclaredMethod(typeof(FejdStartup), "Start", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(AccessTools.DeclaredMethod(typeof(Localizer), "SafeCallLocalizeComplete", (Type[])null, (Type[])null)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } private static byte[]? LoadTranslationFromAssembly(string language) { foreach (string fileExtension in fileExtensions) { byte[] array = ReadEmbeddedFileBytes("translations." + language + fileExtension); if (array != null) { return array; } } return null; } public static byte[]? ReadEmbeddedFileBytes(string resourceFileName, Assembly? containingAssembly = null) { using MemoryStream memoryStream = new MemoryStream(); if ((object)containingAssembly == null) { containingAssembly = Assembly.GetCallingAssembly(); } string text = containingAssembly.GetManifestResourceNames().FirstOrDefault((string str) => str.EndsWith(resourceFileName, StringComparison.Ordinal)); if (text != null) { containingAssembly.GetManifestResourceStream(text)?.CopyTo(memoryStream); } return (memoryStream.Length == 0L) ? null : memoryStream.ToArray(); } } public static class LocalizationManagerVersion { public const string Version = "1.4.1"; } } namespace System.Runtime.CompilerServices { internal static class IsExternalInit { } } namespace FeedLikeGrandma { internal static class FeedConsoleCommands { [CompilerGenerated] private static class <>O { public static ConsoleEvent <0>__WriteReference; public static ConsoleOptionsFetcher <1>__GetEmptyOptions; } private static readonly List EmptyOptions = new List(); private static bool Registered; internal static void Register() { //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Expected O, but got Unknown //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Expected O, but got Unknown if (!Registered) { Registered = true; object obj = <>O.<0>__WriteReference; if (obj == null) { ConsoleEvent val = WriteReference; <>O.<0>__WriteReference = val; obj = (object)val; } object obj2 = <>O.<1>__GetEmptyOptions; if (obj2 == null) { ConsoleOptionsFetcher val2 = GetEmptyOptions; <>O.<1>__GetEmptyOptions = val2; obj2 = (object)val2; } new ConsoleCommand("flg:reference", "Write generated FeedLikeGrandma reference YAML. Usage: flg:reference", (ConsoleEvent)obj, false, false, false, false, false, (ConsoleOptionsFetcher)obj2, false, false, false); } } private static List GetEmptyOptions() { return EmptyOptions; } private static void WriteReference(ConsoleEventArgs args) { if (FeedLikeGrandmaPlugin.TryWriteReferenceConfigurationFile(out string path, out string error)) { Terminal context = args.Context; if (context != null) { context.AddString("Wrote FeedLikeGrandma reference to " + path); } } else { Terminal context2 = args.Context; if (context2 != null) { context2.AddString(error); } } } } internal static class FeedAutoFeedSystem { private const float ContainerCleanupInterval = 5f; private static readonly float[] DropPointAngles = new float[8] { 0f, 45f, -45f, 90f, -90f, 135f, -135f, 180f }; private static readonly List RegisteredFeedContainers = new List(); private static float _nextContainerCleanupTime; internal static void Shutdown() { RegisteredFeedContainers.Clear(); _nextContainerCleanupTime = 0f; } internal static void RegisterContainer(Container? container) { if (IsContainerAlive(container) && FeedBarrelSystem.IsFeedBarrel(container)) { FeedBarrelSystem.ApplyConfiguredContainerSize(container); if (!RegisteredFeedContainers.Contains(container)) { RegisteredFeedContainers.Add(container); } } } internal static void DeregisterContainer(Container? container) { if (!((Object)(object)container == (Object)null)) { RegisteredFeedContainers.Remove(container); } } internal static void RefreshRegisteredContainerSizes() { CleanupContainers(); foreach (Container registeredFeedContainer in RegisteredFeedContainers) { FeedBarrelSystem.ApplyConfiguredContainerSize(registeredFeedContainer); } } internal static bool TryFindClosestConsumableFromFeedBarrel(MonsterAI monsterAI, float maxRange, out ItemDrop? consumable) { //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_00da: Unknown result type (might be due to invalid IL or missing references) //IL_00e6: Unknown result type (might be due to invalid IL or missing references) //IL_00eb: Unknown result type (might be due to invalid IL or missing references) //IL_00ec: Unknown result type (might be due to invalid IL or missing references) //IL_00f1: Unknown result type (might be due to invalid IL or missing references) //IL_015b: Unknown result type (might be due to invalid IL or missing references) //IL_0115: Unknown result type (might be due to invalid IL or missing references) //IL_0128: Unknown result type (might be due to invalid IL or missing references) //IL_012a: Unknown result type (might be due to invalid IL or missing references) consumable = null; if (!FeedBarrelSystem.IsEnabled) { return false; } if ((Object)(object)monsterAI == (Object)null || maxRange <= 0f) { return false; } if (monsterAI.m_consumeItems == null || monsterAI.m_consumeItems.Count == 0) { return false; } CleanupContainersThrottled(); if (RegisteredFeedContainers.Count == 0) { return false; } GameObject gameObject = ((Component)monsterAI).gameObject; if (!FeedSystem.HasRuleFor(gameObject)) { return false; } ZNetView component = gameObject.GetComponent(); Tameable component2 = gameObject.GetComponent(); if ((Object)(object)component == (Object)null || !component.IsValid() || !component.IsOwner() || (Object)(object)component2 == (Object)null || !component2.IsHungry() || ((BaseAI)monsterAI).IsAlerted()) { return false; } Vector3 position = ((Component)monsterAI).transform.position; float sqrRange = maxRange * maxRange; Container val = null; ItemData val2 = null; Vector3 dropPosition = Vector3.zero; float num = float.PositiveInfinity; foreach (Container registeredFeedContainer in RegisteredFeedContainers) { if (IsEligibleContainer(registeredFeedContainer, position, sqrRange)) { Vector3 val3 = GetContainerCenter(registeredFeedContainer) - position; float sqrMagnitude = ((Vector3)(ref val3)).sqrMagnitude; if (!(sqrMagnitude >= num) && TrySelectFood(monsterAI, registeredFeedContainer, out ItemData item) && item != null && TryGetReachableDropPoint(monsterAI, registeredFeedContainer, position, maxRange, out var dropPosition2)) { val = registeredFeedContainer; val2 = item; dropPosition = dropPosition2; num = sqrMagnitude; } } } if ((Object)(object)val != (Object)null && val2 != null && TryRemoveAndDrop(val, val2, dropPosition, out consumable) && (Object)(object)consumable != (Object)null) { return true; } return false; } private static bool TrySelectFood(MonsterAI monsterAI, Container container, out ItemData? item) { item = null; Inventory inventory = container.GetInventory(); if (inventory == null) { return false; } if (!FeedSystem.TrySelectAutoFeedItem(monsterAI, inventory.GetAllItems(), out item) || item == null) { return false; } return EnsureDropPrefab(item); } private static bool TryRemoveAndDrop(Container container, ItemData item, Vector3 dropPosition, out ItemDrop? droppedItem) { //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) droppedItem = null; Inventory inventory = container.GetInventory(); ZNetView nview = container.m_nview; if (inventory == null || (Object)(object)nview == (Object)null || !nview.IsValid()) { return false; } if (!nview.IsOwner()) { nview.ClaimOwnership(); } if (!nview.IsOwner()) { return false; } ItemData val = item.Clone(); val.m_dropPrefab = item.m_dropPrefab; if (!inventory.RemoveOneItem(item)) { return false; } container.Save(); droppedItem = ItemDrop.DropItem(val, 1, dropPosition, ((Component)container).transform.rotation); return (Object)(object)droppedItem != (Object)null; } private static bool TryGetReachableDropPoint(MonsterAI monsterAI, Container container, Vector3 animalPosition, float maxRange, out Vector3 dropPosition) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_006d: 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) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_00c0: Unknown result type (might be due to invalid IL or missing references) //IL_00c5: 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_00c9: Unknown result type (might be due to invalid IL or missing references) //IL_00ca: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: Unknown result type (might be due to invalid IL or missing references) //IL_00d3: Unknown result type (might be due to invalid IL or missing references) //IL_00f4: Unknown result type (might be due to invalid IL or missing references) //IL_00f9: Unknown result type (might be due to invalid IL or missing references) Vector3 containerCenter = GetContainerCenter(container); Vector3 val = (((Object)(object)container.m_piece != (Object)null) ? ((StaticTarget)container.m_piece).FindClosestPoint(animalPosition) : containerCenter); Vector3 val2 = HorizontalNormalized(animalPosition - val); if (((Vector3)(ref val2)).sqrMagnitude <= 0f) { val2 = -((Component)container).transform.forward; } float num = Mathf.Max(1.25f, monsterAI.m_consumeRange * 0.75f); if (TryUseDropPoint(monsterAI, animalPosition, val + val2 * num, maxRange, out dropPosition)) { return true; } float num2 = Mathf.Max(1.5f, monsterAI.m_consumeRange + 0.5f); float[] dropPointAngles = DropPointAngles; foreach (float num3 in dropPointAngles) { Vector3 val3 = Quaternion.Euler(0f, num3, 0f) * val2; if (TryUseDropPoint(monsterAI, animalPosition, containerCenter + val3 * num2, maxRange, out dropPosition)) { return true; } } dropPosition = Vector3.zero; return false; } private static bool TryUseDropPoint(MonsterAI monsterAI, Vector3 animalPosition, Vector3 point, float maxRange, out Vector3 dropPosition) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) point = SnapToGround(point); Vector3 val = point - animalPosition; if (((Vector3)(ref val)).sqrMagnitude > maxRange * maxRange || !((BaseAI)monsterAI).HavePath(point)) { dropPosition = Vector3.zero; return false; } dropPosition = point + Vector3.up * 0.25f; return true; } private static Vector3 GetContainerCenter(Container container) { //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)container.m_piece != (Object)null) { return ((StaticTarget)container.m_piece).GetCenter(); } return ((Component)container).transform.position; } private static Vector3 HorizontalNormalized(Vector3 vector) { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) vector.y = 0f; if (!(((Vector3)(ref vector)).sqrMagnitude > 0.0001f)) { return Vector3.zero; } return ((Vector3)(ref vector)).normalized; } private static Vector3 SnapToGround(Vector3 point) { //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) float y = default(float); if ((Object)(object)ZoneSystem.instance != (Object)null && ZoneSystem.instance.GetGroundHeight(point, ref y)) { point.y = y; } return point; } private static bool IsEligibleContainer(Container container, Vector3 animalPosition, float sqrRange) { //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) if (!IsContainerObjectValid(container)) { return false; } Inventory inventory = container.GetInventory(); if (inventory == null || inventory.NrOfItems() <= 0 || container.IsInUse()) { return false; } if (!FeedBarrelSystem.IsEnabled || !FeedBarrelSystem.IsFeedBarrel(container)) { return false; } Vector3 val = GetContainerCenter(container) - animalPosition; return ((Vector3)(ref val)).sqrMagnitude <= sqrRange; } private static bool IsContainerAlive(Container? container) { return (Object)(object)container != (Object)null; } private static bool IsContainerObjectValid(Container? container) { if (!IsContainerAlive(container)) { return false; } ZNetView nview = container.m_nview; if ((Object)(object)nview == (Object)null || !nview.IsValid() || container.GetInventory() == null) { return false; } return nview.GetZDO().GetLong(ZDOVars.s_creator, 0L) != 0; } private static bool EnsureDropPrefab(ItemData item) { if ((Object)(object)item.m_dropPrefab != (Object)null) { return true; } GameObject dropPrefab = default(GameObject); if (ObjectDB.instance == null || !ObjectDB.instance.TryGetItemPrefab(item.m_shared, ref dropPrefab)) { return false; } item.m_dropPrefab = dropPrefab; return true; } private static void CleanupContainersThrottled() { if (RegisteredFeedContainers.Count != 0 && !(Time.time < _nextContainerCleanupTime)) { _nextContainerCleanupTime = Time.time + 5f; CleanupContainers(); } } private static void CleanupContainers() { RegisteredFeedContainers.RemoveAll((Container container) => !IsContainerAlive(container) || !FeedBarrelSystem.IsFeedBarrel(container)); } } internal static class FeedBarrelSystem { private sealed class RendererMaterialSnapshot { public Renderer Renderer { get; } public Material[] Materials { get; } public RendererMaterialSnapshot(Renderer renderer, Material[] materials) { Renderer = renderer; Materials = materials; } } internal const string PrefabName = "FLG_FeedBarrel"; private const string SourcePrefabName = "piece_chest_barrel"; private const string EctoplasmMaterialName = "ectoplasm_mat"; private const int DefaultColumns = 6; private const int DefaultRows = 2; private const int MinColumns = 6; private const int MaxColumns = 8; private const int MinRows = 2; private const int MaxRows = 100; private const int IconSize = 128; private const int IconLayer = 30; private static GameObject? _prefab; private static GameObject? _prefabContainer; private static readonly Dictionary MaterialLookupCache = new Dictionary(StringComparer.OrdinalIgnoreCase); private static readonly HashSet MaterialLookupMissCache = new HashSet(StringComparer.OrdinalIgnoreCase); private static List? _originalMaterials; private static Sprite? _originalIcon; private static Sprite? _ectoplasmIcon; private static bool _materialLookupCacheBuilt; internal static bool IsEnabled => !string.IsNullOrWhiteSpace(FeedLikeGrandmaPlugin.FeedBarrelRecipe.Value); internal static void OnGameDataChanged() { MaterialLookupCache.Clear(); MaterialLookupMissCache.Clear(); _materialLookupCacheBuilt = false; } internal static void RegisterPrefabsAndPieces(ObjectDB? objectDb = null) { GameObject val = EnsurePrefab(); if (val != null) { ConfigurePrefab(val, objectDb ?? ObjectDB.instance); if (ZNetScene.instance != null) { RegisterWithZNetScene(ZNetScene.instance, val); } RegisterWithHammer(objectDb ?? ObjectDB.instance, val); FeedAutoFeedSystem.RefreshRegisteredContainerSizes(); } } internal static bool IsFeedBarrel(Container? container) { if ((Object)(object)container == (Object)null) { return false; } return IsFeedBarrel(((Component)container).gameObject); } internal static bool IsFeedBarrel(GameObject? gameObject) { if ((Object)(object)gameObject == (Object)null) { return false; } return string.Equals(Utils.GetPrefabName(gameObject), "FLG_FeedBarrel", StringComparison.Ordinal); } private static GameObject? EnsurePrefab() { if (_prefab != null) { return _prefab; } GameObject val = FindScenePrefab("FLG_FeedBarrel"); if (val != null) { _prefab = val; return _prefab; } GameObject val2 = FindScenePrefab("piece_chest_barrel"); if (val2 == null) { return null; } GameObject val3 = Object.Instantiate(val2, EnsurePrefabContainer().transform); ((Object)val3).name = "FLG_FeedBarrel"; if (val3.GetComponent() == null || val3.GetComponent() == null || val3.GetComponent() == null) { FeedLikeGrandmaPlugin.Log.LogWarning((object)"Could not create FLG_FeedBarrel: piece_chest_barrel did not have the expected Piece, Container, and ZNetView components."); Object.Destroy((Object)(object)val3); return null; } _prefab = val3; return _prefab; } private static void ConfigurePrefab(GameObject prefab, ObjectDB? objectDb) { //IL_002f: Unknown result type (might be due to invalid IL or missing references) Piece component = prefab.GetComponent(); if (component != null) { bool isEnabled = IsEnabled; component.m_name = "$flg_feed_barrel"; component.m_description = "$flg_feed_barrel_description"; component.m_enabled = isEnabled; component.m_category = (PieceCategory)0; StoreOriginalIcon(component); StoreOriginalMaterials(prefab); ApplyConfiguredMaterial(prefab, component); if (objectDb != null) { component.m_resources = (isEnabled ? BuildRequirements(objectDb, FeedLikeGrandmaPlugin.FeedBarrelRecipe.Value).ToArray() : Array.Empty()); } } Container component2 = prefab.GetComponent(); if (component2 != null) { component2.m_name = "$flg_feed_barrel"; ApplyConfiguredContainerSize(component2); } } internal static void ApplyConfiguredContainerSize(Container? container) { if (container != null && IsFeedBarrel(container)) { GetConfiguredContainerSize(out var columns, out var rows); container.m_width = columns; container.m_height = rows; Inventory inventory = container.GetInventory(); if (inventory != null) { inventory.m_width = columns; inventory.m_height = rows; } } } private static void ApplyConfiguredMaterial(GameObject prefab, Piece piece) { RestoreOriginalMaterials(); piece.m_icon = _originalIcon; if (FeedLikeGrandmaPlugin.FeedBarrelMaterialStyle.Value != FeedLikeGrandmaPlugin.FeedBarrelMaterial.Ectoplasm) { return; } Material val = ResolveMaterial("ectoplasm_mat"); if (val == null) { FeedLikeGrandmaPlugin.Log.LogWarning((object)"FLG_FeedBarrel material 'ectoplasm_mat' was not found. Using default barrel material."); return; } ApplyMaterialOverride(prefab, val); if (_ectoplasmIcon == null) { _ectoplasmIcon = RenderPieceIcon(prefab); } if (_ectoplasmIcon != null) { piece.m_icon = _ectoplasmIcon; } } private static void GetConfiguredContainerSize(out int columns, out int rows) { columns = ((FeedLikeGrandmaPlugin.FeedBarrelContainerColumns != null) ? FeedLikeGrandmaPlugin.FeedBarrelContainerColumns.Value : 6); rows = ((FeedLikeGrandmaPlugin.FeedBarrelContainerRows != null) ? FeedLikeGrandmaPlugin.FeedBarrelContainerRows.Value : 2); columns = Mathf.Clamp(columns, 6, 8); rows = Mathf.Clamp(rows, 2, 100); } private static void RegisterWithZNetScene(ZNetScene zNetScene, GameObject prefab) { int stableHashCode = StringExtensionMethods.GetStableHashCode("FLG_FeedBarrel"); if ((Object)(object)prefab.GetComponent() != (Object)null) { if (zNetScene.m_prefabs.All((GameObject existing) => (Object)(object)existing == (Object)null || ((Object)existing).name != "FLG_FeedBarrel")) { zNetScene.m_prefabs.Add(prefab); } } else if (zNetScene.m_nonNetViewPrefabs.All((GameObject existing) => (Object)(object)existing == (Object)null || ((Object)existing).name != "FLG_FeedBarrel")) { zNetScene.m_nonNetViewPrefabs.Add(prefab); } if (!zNetScene.m_namedPrefabs.ContainsKey(stableHashCode)) { zNetScene.m_namedPrefabs.Add(stableHashCode, prefab); } else if ((Object)(object)zNetScene.m_namedPrefabs[stableHashCode] == (Object)null || ((Object)zNetScene.m_namedPrefabs[stableHashCode]).name == "FLG_FeedBarrel") { zNetScene.m_namedPrefabs[stableHashCode] = prefab; } } private static void RegisterWithHammer(ObjectDB? objectDb, GameObject prefab) { if (objectDb == null) { return; } GameObject itemPrefab = objectDb.GetItemPrefab("Hammer"); PieceTable val = (((Object)(object)itemPrefab == (Object)null) ? null : itemPrefab.GetComponent())?.m_itemData.m_shared.m_buildPieces; if (val != null) { val.m_pieces.RemoveAll((GameObject existing) => (Object)(object)existing == (Object)null || ((Object)existing).name == "FLG_FeedBarrel"); if (!IsEnabled) { RefreshLocalBuildTable(val); return; } val.m_pieces.Add(prefab); RefreshLocalBuildTable(val); } } private static void RefreshLocalBuildTable(PieceTable pieceTable) { if (Player.m_localPlayer != null && Player.m_localPlayer.m_buildPieces != null && (Object)(object)Player.m_localPlayer.m_buildPieces == (Object)(object)pieceTable) { ((Humanoid)Player.m_localPlayer).SetPlaceMode(pieceTable); } } private static IEnumerable BuildRequirements(ObjectDB objectDb, string recipeText) { if (string.IsNullOrWhiteSpace(recipeText)) { yield break; } string[] array = recipeText.Split(new char[1] { ',' }, StringSplitOptions.RemoveEmptyEntries); for (int i = 0; i < array.Length; i++) { string[] array2 = array[i].Trim().Split(new char[1] { ':' }, StringSplitOptions.RemoveEmptyEntries); if (array2.Length != 0) { string text = array2[0].Trim(); int result = 1; if (array2.Length > 1) { int.TryParse(array2[1].Trim(), NumberStyles.Integer, CultureInfo.InvariantCulture, out result); result = Mathf.Max(1, result); } GameObject val = FindItemPrefab(objectDb, text); ItemDrop val2 = (((Object)(object)val == (Object)null) ? null : val.GetComponent()); if (val2 == null) { FeedLikeGrandmaPlugin.Log.LogWarning((object)("FLG_FeedBarrel build ingredient '" + text + "' was not found.")); continue; } yield return new Requirement { m_resItem = val2, m_amount = result, m_amountPerLevel = 0, m_recover = true }; } } } private static GameObject? FindItemPrefab(ObjectDB objectDb, string itemName) { GameObject itemPrefab = objectDb.GetItemPrefab(itemName); if (itemPrefab != null) { return itemPrefab; } int stableHashCode = StringExtensionMethods.GetStableHashCode(itemName); if (!objectDb.m_itemByHash.TryGetValue(stableHashCode, out var value)) { return null; } return value; } private static GameObject? FindScenePrefab(string prefabName) { if (ZNetScene.instance == null) { return null; } GameObject prefab = ZNetScene.instance.GetPrefab(prefabName); if (prefab != null) { return prefab; } int stableHashCode = StringExtensionMethods.GetStableHashCode(prefabName); if (!ZNetScene.instance.m_namedPrefabs.TryGetValue(stableHashCode, out var value)) { return null; } return value; } private static void StoreOriginalIcon(Piece piece) { if (_originalIcon == null) { _originalIcon = piece.m_icon; } } private static void StoreOriginalMaterials(GameObject prefab) { if (_originalMaterials == null) { _originalMaterials = (from renderer in GetVisualRenderers(prefab) select new RendererMaterialSnapshot(renderer, renderer.sharedMaterials.ToArray())).ToList(); } } private static void RestoreOriginalMaterials() { if (_originalMaterials == null) { return; } foreach (RendererMaterialSnapshot originalMaterial in _originalMaterials) { if (originalMaterial.Renderer != null) { originalMaterial.Renderer.sharedMaterials = originalMaterial.Materials.ToArray(); } } } private static void ApplyMaterialOverride(GameObject prefab, Material material) { foreach (Renderer visualRenderer in GetVisualRenderers(prefab)) { Material[] sharedMaterials = visualRenderer.sharedMaterials; if (sharedMaterials.Length != 0) { Material[] array = (Material[])(object)new Material[sharedMaterials.Length]; for (int i = 0; i < array.Length; i++) { array[i] = material; } visualRenderer.sharedMaterials = array; } } } private static Material? ResolveMaterial(string materialName) { string text = NormalizeMaterialName(materialName); if (text.Length == 0 || MaterialLookupMissCache.Contains(text)) { return null; } EnsureMaterialLookupCache(force: false); if (TryResolveCachedMaterial(materialName, text, out Material material)) { return material; } EnsureMaterialLookupCache(force: true); if (TryResolveCachedMaterial(materialName, text, out material)) { return material; } MaterialLookupMissCache.Add(text); return null; } private static bool TryResolveCachedMaterial(string materialName, string normalizedName, out Material? material) { if (MaterialLookupCache.TryGetValue(materialName, out Material value) && value != null) { material = value; return true; } if (MaterialLookupCache.TryGetValue(normalizedName, out Material value2) && value2 != null) { material = value2; return true; } material = null; return false; } private static void EnsureMaterialLookupCache(bool force) { if (_materialLookupCacheBuilt && !force) { return; } MaterialLookupCache.Clear(); MaterialLookupMissCache.Clear(); Material[] array = Resources.FindObjectsOfTypeAll(); foreach (Material val in array) { if (val != null && !string.IsNullOrWhiteSpace(((Object)val).name)) { if (!MaterialLookupCache.ContainsKey(((Object)val).name)) { MaterialLookupCache[((Object)val).name] = val; } string text = NormalizeMaterialName(((Object)val).name); if (text.Length > 0 && !MaterialLookupCache.ContainsKey(text)) { MaterialLookupCache[text] = val; } } } _materialLookupCacheBuilt = true; } private static string NormalizeMaterialName(string name) { return (name ?? string.Empty).Replace(" (Instance)", string.Empty).Trim(); } private static Sprite? RenderPieceIcon(GameObject sourcePrefab) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Invalid comparison between Unknown and I4 //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: Unknown result type (might be due to invalid IL or missing references) //IL_0115: Unknown result type (might be due to invalid IL or missing references) //IL_0137: Unknown result type (might be due to invalid IL or missing references) //IL_0148: Unknown result type (might be due to invalid IL or missing references) //IL_0167: Unknown result type (might be due to invalid IL or missing references) //IL_018e: Unknown result type (might be due to invalid IL or missing references) //IL_0195: Unknown result type (might be due to invalid IL or missing references) //IL_01d6: Unknown result type (might be due to invalid IL or missing references) //IL_01dd: Unknown result type (might be due to invalid IL or missing references) //IL_01e4: Unknown result type (might be due to invalid IL or missing references) //IL_020f: Unknown result type (might be due to invalid IL or missing references) //IL_023b: Unknown result type (might be due to invalid IL or missing references) //IL_0257: Unknown result type (might be due to invalid IL or missing references) //IL_025c: Unknown result type (might be due to invalid IL or missing references) //IL_0269: Expected O, but got Unknown //IL_0286: Unknown result type (might be due to invalid IL or missing references) //IL_0298: Unknown result type (might be due to invalid IL or missing references) //IL_02a4: Unknown result type (might be due to invalid IL or missing references) if ((int)SystemInfo.graphicsDeviceType == 4) { return null; } GameObject val = null; Camera val2 = null; Light val3 = null; RenderTexture val4 = null; RenderTexture active = RenderTexture.active; try { val = SpawnIconRenderClone(sourcePrefab, out List renderers); if (val == null || renderers.Count == 0) { return null; } Bounds bounds = renderers[0].bounds; foreach (Renderer item in renderers.Skip(1)) { ((Bounds)(ref bounds)).Encapsulate(item.bounds); } Vector3 size = ((Bounds)(ref bounds)).size; Transform transform = val.transform; transform.position -= ((Bounds)(ref bounds)).center; val2 = new GameObject("FeedLikeGrandma Feed Barrel Icon Camera", new Type[1] { typeof(Camera) }).GetComponent(); val2.backgroundColor = Color.clear; val2.clearFlags = (CameraClearFlags)2; val2.fieldOfView = 0.5f; val2.farClipPlane = 10000000f; val2.cullingMask = 1073741824; ((Component)val2).transform.rotation = Quaternion.Euler(0f, 180f, 0f); val3 = new GameObject("FeedLikeGrandma Feed Barrel Icon Light", new Type[1] { typeof(Light) }).GetComponent(); ((Component)val3).transform.position = Vector3.zero; ((Component)val3).transform.rotation = Quaternion.Euler(5f, 180f, 5f); val3.type = (LightType)1; val3.cullingMask = 1073741824; val3.intensity = 1.3f; float num = (Mathf.Max(size.x, size.y) + 0.1f) / Mathf.Tan(val2.fieldOfView * ((float)Math.PI / 180f)); if (float.IsNaN(num) || float.IsInfinity(num) || num <= 0f) { num = Mathf.Max(size.x, Mathf.Max(size.y, size.z)) + 1f; } ((Component)val2).transform.position = new Vector3(0f, 0f, num); val4 = (RenderTexture.active = (val2.targetTexture = RenderTexture.GetTemporary(128, 128, 24, (RenderTextureFormat)0))); GL.Clear(true, true, Color.clear); val2.Render(); Texture2D val6 = new Texture2D(128, 128, (TextureFormat)4, false) { name = "FLG_FeedBarrel_Icon" }; Rect val7 = default(Rect); ((Rect)(ref val7))..ctor(0f, 0f, 128f, 128f); val6.ReadPixels(val7, 0, 0); val6.Apply(); Sprite obj = Sprite.Create(val6, val7, new Vector2(0.5f, 0.5f), 100f); ((Object)obj).name = ((Object)val6).name; return obj; } catch (Exception ex) { FeedLikeGrandmaPlugin.Log.LogWarning((object)("FLG_FeedBarrel icon render failed: " + ex.Message)); return null; } finally { RenderTexture.active = active; if (val2 != null) { val2.targetTexture = null; DestroyTemporaryObject(((Component)val2).gameObject); } if (val3 != null) { DestroyTemporaryObject(((Component)val3).gameObject); } if ((Object)(object)val4 != (Object)null) { RenderTexture.ReleaseTemporary(val4); } if (val != null) { DestroyTemporaryObject(val); } } } private static GameObject? SpawnIconRenderClone(GameObject sourcePrefab, out List renderers) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Expected O, but got Unknown //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) renderers = new List(); GameObject val = null; try { val = new GameObject("FLG_FeedBarrel_IconRoot"); val.SetActive(false); GameObject val2 = Object.Instantiate(sourcePrefab, val.transform, false); ((Object)val2).name = "FLG_FeedBarrel_IconRender"; StripIconRenderClone(val2); val2.transform.SetParent((Transform)null, false); Object.DestroyImmediate((Object)(object)val); val = null; val2.transform.position = Vector3.zero; val2.transform.rotation = Quaternion.Euler(23f, 51f, 25.8f); SetLayerRecursive(val2, 30); val2.SetActive(true); renderers = (from renderer in GetVisualRenderers(val2) where ((Component)renderer).gameObject.activeInHierarchy select renderer).ToList(); if (renderers.Count == 0) { DestroyTemporaryObject(val2); return null; } HashSet hashSet = new HashSet(renderers); Renderer[] componentsInChildren = val2.GetComponentsInChildren(true); foreach (Renderer val3 in componentsInChildren) { if (val3 != null && !hashSet.Contains(val3)) { val3.enabled = false; } } return val2; } catch { if (val != null) { Object.DestroyImmediate((Object)(object)val); } throw; } } private static void StripIconRenderClone(GameObject renderObject) { Transform[] componentsInChildren = renderObject.GetComponentsInChildren(true); for (int i = 0; i < componentsInChildren.Length; i++) { Component[] components = ((Component)componentsInChildren[i]).GetComponents(); for (int num = components.Length - 1; num >= 0; num--) { Component val = components[num]; if (!((Object)(object)val == (Object)null) && !(val is Transform) && !(val is MeshFilter) && !(val is MeshRenderer) && !(val is SkinnedMeshRenderer)) { try { Object.DestroyImmediate((Object)(object)val); } catch (Exception ex) { FeedLikeGrandmaPlugin.Log.LogDebug((object)("Could not strip icon render component '" + ((object)val).GetType().Name + "' from '" + ((Object)renderObject).name + "': " + ex.Message)); } } } } } private static List GetVisualRenderers(GameObject prefab) { return (from renderer in prefab.GetComponentsInChildren(true) where (Object)(object)renderer != (Object)null && renderer.enabled && !((object)renderer).GetType().Name.Equals("ParticleSystemRenderer", StringComparison.Ordinal) select renderer).ToList(); } private static void SetLayerRecursive(GameObject gameObject, int layer) { //IL_001b: Unknown result type (might be due to invalid IL or missing references) gameObject.layer = layer; foreach (Transform item in gameObject.transform) { SetLayerRecursive(((Component)item).gameObject, layer); } } private static void DestroyTemporaryObject(GameObject? gameObject) { if (gameObject == null) { return; } try { gameObject.SetActive(false); Object.DestroyImmediate((Object)(object)gameObject); } catch (Exception ex) { FeedLikeGrandmaPlugin.Log.LogDebug((object)("Could not immediately destroy icon render object '" + ((Object)gameObject).name + "': " + ex.Message)); Object.Destroy((Object)(object)gameObject); } } private static GameObject EnsurePrefabContainer() { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Expected O, but got Unknown if (_prefabContainer != null) { return _prefabContainer; } _prefabContainer = new GameObject("FeedLikeGrandma Feed Barrel Prefabs"); _prefabContainer.SetActive(false); Object.DontDestroyOnLoad((Object)(object)_prefabContainer); return _prefabContainer; } } internal static class FeedBetterRidingCompatibility { private const string BetterRidingPluginTypeName = "BetterRiding.BetterRidingPlugin"; private const string MountStaminaSystemTypeName = "BetterRiding.Behaviour.MountStaminaSystem"; private const string MountAttackControllerTypeName = "BetterRiding.Behaviour.MountAttackController"; private const string BetterRidingBlockFlyingDismountPatchTypeName = "BetterRiding.Behaviour.BlockFlyingDismountPatch"; private const string DragonRidersBlockFlyingDismountPatchTypeName = "DragonRiders.Patches.MountController.BlockFlyingDismountPatch"; private const float AttackRecordCleanupInterval = 60f; private const float AttackRecordTtl = 1800f; private static readonly Dictionary LastAttackTimes = new Dictionary(); private static readonly Dictionary?> BetterRidingBoolConfigEntries = new Dictionary>(StringComparer.Ordinal); private static readonly List ExpiredAttackKeys = new List(); private static float _nextAttackRecordCleanupTime; private static bool _betterRidingPluginTypeSearched; private static Type? _betterRidingPluginType; private static bool _mountStaminaSystemTypeSearched; private static Type? _mountStaminaSystemType; private static bool _mountStaminaSaddleFieldSearched; private static FieldInfo? _mountStaminaSaddleField; private static int _mountAttackSafetySaddleId; private static int _mountAttackSafetyCharacterId; private static int _mountAttackSafetyFrame; private static bool _mountAttackSafetyAllowed = true; private static Type? BetterRidingPluginType { get { if (!_betterRidingPluginTypeSearched) { _betterRidingPluginType = FindType("BetterRiding.BetterRidingPlugin"); _betterRidingPluginTypeSearched = true; } return _betterRidingPluginType; } } private static Type? MountStaminaSystemType { get { if (!_mountStaminaSystemTypeSearched) { _mountStaminaSystemType = FindType("BetterRiding.Behaviour.MountStaminaSystem"); _mountStaminaSystemTypeSearched = true; } return _mountStaminaSystemType; } } internal static bool HasBetterRiding => BetterRidingPluginType != null; internal static bool HasBetterRidingMountStaminaSystem => MountStaminaSystemType != null; internal static void Shutdown() { LastAttackTimes.Clear(); BetterRidingBoolConfigEntries.Clear(); ExpiredAttackKeys.Clear(); _nextAttackRecordCleanupTime = 0f; _mountAttackSafetySaddleId = 0; _mountAttackSafetyCharacterId = 0; _mountAttackSafetyFrame = 0; _mountAttackSafetyAllowed = true; _betterRidingPluginTypeSearched = false; _betterRidingPluginType = null; _mountStaminaSystemTypeSearched = false; _mountStaminaSystemType = null; _mountStaminaSaddleFieldSearched = false; _mountStaminaSaddleField = null; } private static Type? FindType(string typeName) { Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); for (int i = 0; i < assemblies.Length; i++) { Type type = assemblies[i].GetType(typeName, throwOnError: false); if (type != null) { return type; } } return null; } internal static MethodBase? GetMountStaminaSystemMethod(string methodName) { Type mountStaminaSystemType = MountStaminaSystemType; if (!(mountStaminaSystemType == null)) { return AccessTools.Method(mountStaminaSystemType, methodName, (Type[])null, (Type[])null); } return null; } internal static MethodBase? GetMountAttackUpdate() { Type type = FindType("BetterRiding.Behaviour.MountAttackController"); if (!(type == null)) { return AccessTools.Method(type, "Update", (Type[])null, (Type[])null); } return null; } internal static MethodBase? GetBetterRidingBlockFlyingDismountPrefix() { Type type = FindType("BetterRiding.Behaviour.BlockFlyingDismountPatch"); if (!(type == null)) { return AccessTools.Method(type, "Prefix", (Type[])null, (Type[])null); } return null; } internal static MethodBase? GetDragonRidersBlockFlyingDismountPrefix() { Type type = FindType("DragonRiders.Patches.MountController.BlockFlyingDismountPatch"); if (!(type == null)) { return AccessTools.Method(type, "Prefix", (Type[])null, (Type[])null); } return null; } internal static bool HandleMountedAttack(Humanoid humanoid, ref bool result) { if (!TryFindAttackSaddle(humanoid, out Sadle saddle) || !TryGetMountStamina(saddle, out ResolvedMountStaminaValues mountStamina) || mountStamina == null) { return true; } float time = Time.time; CleanupAttackRecords(time); int mountKey = GetMountKey(saddle); if (mountKey != 0 && LastAttackTimes.TryGetValue(mountKey, out var value) && time - value < mountStamina.AttackCooldown) { result = false; return false; } if (!CanUseAttackStamina(saddle, mountStamina)) { result = false; return false; } UseAttackStamina(saddle, mountStamina); if (mountKey != 0) { LastAttackTimes[mountKey] = time; } result = true; return false; } private static void CleanupAttackRecords(float now) { if (LastAttackTimes.Count == 0 || now < _nextAttackRecordCleanupTime) { return; } _nextAttackRecordCleanupTime = now + 60f; ExpiredAttackKeys.Clear(); foreach (KeyValuePair lastAttackTime in LastAttackTimes) { if (now - lastAttackTime.Value > 1800f) { ExpiredAttackKeys.Add(lastAttackTime.Key); } } foreach (int expiredAttackKey in ExpiredAttackKeys) { LastAttackTimes.Remove(expiredAttackKey); } ExpiredAttackKeys.Clear(); } internal static void SuppressPlayerAttackControlsWhileRiding(Player player, ref bool attack, ref bool attackHold, ref bool secondaryAttack, ref bool secondaryAttackHold) { if (player != null && ((Character)player).IsRiding()) { attack = false; attackHold = false; secondaryAttack = false; secondaryAttackHold = false; ResetPlayerDrawAttack(player); } } internal static bool AllowPlayerAttackInput(Player player) { if (player == null || !((Character)player).IsRiding()) { return true; } ResetPlayerDrawAttack(player); return false; } internal static bool CanAttack(object mountStaminaSystem, ref bool result) { if (!TryGetSystemSaddle(mountStaminaSystem, out Sadle saddle) || !TryGetMountStamina(saddle, out ResolvedMountStaminaValues mountStamina) || mountStamina == null) { return true; } result = CanUseAttackStamina(saddle, mountStamina); return false; } internal static bool CanJump(object mountStaminaSystem, ref bool result) { if (!TryGetSystemSaddle(mountStaminaSystem, out Sadle saddle) || !TryGetMountStamina(saddle, out ResolvedMountStaminaValues mountStamina) || mountStamina == null) { return true; } result = CanUseJumpStamina(saddle, mountStamina); return false; } internal static bool OnAttackStart(object mountStaminaSystem) { if (!TryGetSystemSaddle(mountStaminaSystem, out Sadle saddle) || !TryGetMountStamina(saddle, out ResolvedMountStaminaValues mountStamina) || mountStamina == null) { return true; } UseAttackStamina(saddle, mountStamina); return false; } internal static bool OnJumpStart(object mountStaminaSystem) { if (!TryGetSystemSaddle(mountStaminaSystem, out Sadle saddle) || !TryGetMountStamina(saddle, out ResolvedMountStaminaValues mountStamina) || mountStamina == null) { return true; } UseJumpStamina(saddle, mountStamina); return false; } internal static bool AllowMountAttackUpdate() { Player localPlayer = Player.m_localPlayer; if (localPlayer == null) { return true; } IDoodadController doodadController = localPlayer.GetDoodadController(); Sadle val = (Sadle)(object)((doodadController is Sadle) ? doodadController : null); if (val == null) { return true; } int instanceID = ((Object)val).GetInstanceID(); Character character = val.GetCharacter(); if (character == null) { return true; } int instanceID2 = ((Object)character).GetInstanceID(); if (instanceID == _mountAttackSafetySaddleId && instanceID2 == _mountAttackSafetyCharacterId && Time.frameCount - _mountAttackSafetyFrame <= (_mountAttackSafetyAllowed ? 30 : 5)) { return _mountAttackSafetyAllowed; } bool num = IsMountAttackStateSafe(character); _mountAttackSafetySaddleId = instanceID; _mountAttackSafetyCharacterId = instanceID2; _mountAttackSafetyFrame = Time.frameCount; _mountAttackSafetyAllowed = num; return num; } internal static bool AllowBlockFlyingDismount(Player player, ref bool result) { if (!ShouldAllowWaterDismount(player)) { return true; } result = true; return false; } private static bool IsMountAttackStateSafe(Character character) { Humanoid val = (Humanoid)(object)((character is Humanoid) ? character : null); if (val == null) { return true; } if (character.m_baseAI != null && val.m_defaultItems != null) { return val.m_defaultItems.Length >= 2; } return false; } private static bool ShouldAllowWaterDismount(Player player) { if (player == null || !((Character)player).IsRiding()) { return false; } IDoodadController doodadController = player.m_doodadController; IDoodadController obj = ((doodadController is Sadle) ? doodadController : null); Character character = ((obj != null) ? ((Sadle)obj).GetCharacter() : null); if (obj != null) { if (!IsInWater((Character?)(object)player)) { return IsInWater(character); } return true; } return false; } private static bool IsInWater(Character? character) { //IL_0027: Unknown result type (might be due to invalid IL or missing references) if (character == null) { return false; } if (!character.InWater()) { if (character.m_waterLevel > character.m_tarLevel) { return character.m_waterLevel - ((Component)character).transform.position.y > 0.05f; } return false; } return true; } private static void ResetPlayerDrawAttack(Player player) { if (!(((Humanoid)player).m_attackDrawTime <= 0f)) { string text = ((Humanoid)player).GetCurrentWeapon()?.m_shared.m_attack.m_drawAnimationState; if (!string.IsNullOrEmpty(text)) { ((Character)player).m_zanim.SetBool(text, false); ((Character)player).m_zanim.SetFloat("drawpercent", 0f); } ((Humanoid)player).m_attackDrawTime = 0f; } } private static bool CanUseAttackStamina(Sadle? saddle, ResolvedMountStaminaValues mountStamina) { if (GetBetterRidingBoolConfig("EnableAttackStaminaDrain", fallback: true)) { return CanUseStamina(saddle, mountStamina.AttackStaminaCost); } return true; } private static bool CanUseJumpStamina(Sadle? saddle, ResolvedMountStaminaValues mountStamina) { if (GetBetterRidingBoolConfig("EnableJumpStaminaDrain", fallback: true)) { return CanUseStamina(saddle, mountStamina.JumpStaminaCost); } return true; } private static void UseAttackStamina(Sadle? saddle, ResolvedMountStaminaValues mountStamina) { if (GetBetterRidingBoolConfig("EnableAttackStaminaDrain", fallback: true)) { UseStaminaDirect(saddle, mountStamina.AttackStaminaCost); } } private static void UseJumpStamina(Sadle? saddle, ResolvedMountStaminaValues mountStamina) { if (GetBetterRidingBoolConfig("EnableJumpStaminaDrain", fallback: true)) { UseStaminaDirect(saddle, mountStamina.JumpStaminaCost); } } private static bool CanUseStamina(Sadle? saddle, float amount) { if (saddle != null && saddle.m_nview != null && saddle.m_nview.IsValid()) { return saddle.GetStamina() >= amount; } return false; } private static void UseStaminaDirect(Sadle? saddle, float amount) { if (!(amount <= 0f) && saddle != null && saddle.m_nview != null && saddle.m_nview.IsValid() && saddle.m_nview.IsOwner()) { float stamina = saddle.GetStamina(); if (!(stamina < amount)) { saddle.SetStamina(Mathf.Max(0f, stamina - amount)); } } } private static bool TryGetSystemSaddle(object mountStaminaSystem, out Sadle? saddle) { saddle = null; if (mountStaminaSystem == null) { return false; } FieldInfo mountStaminaSaddleField = GetMountStaminaSaddleField(); if (mountStaminaSaddleField == null) { return false; } object? value = mountStaminaSaddleField.GetValue(mountStaminaSystem); saddle = (Sadle?)((value is Sadle) ? value : null); return saddle != null; } private static bool TryFindAttackSaddle(Humanoid humanoid, out Sadle? saddle) { saddle = null; if (humanoid == null) { return false; } Player val = (Player)(object)((humanoid is Player) ? humanoid : null); if (val != null && ((Character)val).IsRiding()) { IDoodadController doodadController = val.m_doodadController; saddle = (Sadle?)(object)((doodadController is Sadle) ? doodadController : null); return saddle != null; } Player localPlayer = Player.m_localPlayer; if (localPlayer == null || !((Character)localPlayer).IsRiding()) { return false; } IDoodadController doodadController2 = localPlayer.m_doodadController; saddle = (Sadle?)(object)((doodadController2 is Sadle) ? doodadController2 : null); Sadle? obj = saddle; Character val2 = ((obj != null) ? obj.GetCharacter() : null); if (val2 == null || (object)val2 != humanoid) { saddle = null; return false; } return true; } private static bool TryGetMountStamina(Sadle? saddle, out ResolvedMountStaminaValues? mountStamina) { mountStamina = null; if (saddle == null) { return false; } Character character = saddle.GetCharacter(); if (character != null) { return FeedSystem.TryGetMountStamina(character, out mountStamina); } return false; } private static int GetMountKey(Sadle? saddle) { if (saddle == null) { return 0; } Character character = saddle.GetCharacter(); if (character != null) { return ((Object)((Component)character).gameObject).GetInstanceID(); } return 0; } private static bool GetBetterRidingBoolConfig(string fieldName, bool fallback) { if (!BetterRidingBoolConfigEntries.TryGetValue(fieldName, out ConfigEntry value)) { Type betterRidingPluginType = BetterRidingPluginType; value = ((betterRidingPluginType == null) ? null : AccessTools.Field(betterRidingPluginType, fieldName)?.GetValue(null)) as ConfigEntry; BetterRidingBoolConfigEntries[fieldName] = value; } return value?.Value ?? fallback; } private static FieldInfo? GetMountStaminaSaddleField() { if (!_mountStaminaSaddleFieldSearched) { Type mountStaminaSystemType = MountStaminaSystemType; _mountStaminaSaddleField = ((mountStaminaSystemType == null) ? null : AccessTools.Field(mountStaminaSystemType, "m_saddle")); _mountStaminaSaddleFieldSearched = true; } return _mountStaminaSaddleField; } } [HarmonyPatch(typeof(Player), "SetControls")] internal static class BetterRidingSuppressPlayerAttackHoldPatch { private static bool Prepare() { return FeedBetterRidingCompatibility.HasBetterRiding; } [HarmonyPriority(0)] [HarmonyAfter(new string[] { "Yggdrah.BetterRiding" })] private static void Prefix(Player __instance, ref bool attack, ref bool attackHold, ref bool secondaryAttack, ref bool secondaryAttackHold) { FeedBetterRidingCompatibility.SuppressPlayerAttackControlsWhileRiding(__instance, ref attack, ref attackHold, ref secondaryAttack, ref secondaryAttackHold); } } [HarmonyPatch(typeof(Player), "PlayerAttackInput")] internal static class BetterRidingSuppressPlayerAttackInputPatch { private static bool Prepare() { return FeedBetterRidingCompatibility.HasBetterRiding; } private static bool Prefix(Player __instance) { return FeedBetterRidingCompatibility.AllowPlayerAttackInput(__instance); } } [HarmonyPatch] internal static class BetterRidingHandleMountedAttackPatch { private static bool Prepare() { return FeedBetterRidingCompatibility.GetMountStaminaSystemMethod("HandleMountedAttack") != null; } private static MethodBase? TargetMethod() { return FeedBetterRidingCompatibility.GetMountStaminaSystemMethod("HandleMountedAttack"); } private static bool Prefix(Humanoid instance, ref bool __result) { return FeedBetterRidingCompatibility.HandleMountedAttack(instance, ref __result); } } [HarmonyPatch] internal static class BetterRidingCanAttackPatch { private static bool Prepare() { return FeedBetterRidingCompatibility.GetMountStaminaSystemMethod("CanAttack") != null; } private static MethodBase? TargetMethod() { return FeedBetterRidingCompatibility.GetMountStaminaSystemMethod("CanAttack"); } private static bool Prefix(object __instance, ref bool __result) { return FeedBetterRidingCompatibility.CanAttack(__instance, ref __result); } } [HarmonyPatch] internal static class BetterRidingCanJumpPatch { private static bool Prepare() { return FeedBetterRidingCompatibility.GetMountStaminaSystemMethod("CanJump") != null; } private static MethodBase? TargetMethod() { return FeedBetterRidingCompatibility.GetMountStaminaSystemMethod("CanJump"); } private static bool Prefix(object __instance, ref bool __result) { return FeedBetterRidingCompatibility.CanJump(__instance, ref __result); } } [HarmonyPatch] internal static class BetterRidingOnAttackStartPatch { private static bool Prepare() { return FeedBetterRidingCompatibility.GetMountStaminaSystemMethod("OnAttackStart") != null; } private static MethodBase? TargetMethod() { return FeedBetterRidingCompatibility.GetMountStaminaSystemMethod("OnAttackStart"); } private static bool Prefix(object __instance) { return FeedBetterRidingCompatibility.OnAttackStart(__instance); } } [HarmonyPatch] internal static class BetterRidingOnJumpStartPatch { private static bool Prepare() { return FeedBetterRidingCompatibility.GetMountStaminaSystemMethod("OnJumpStart") != null; } private static MethodBase? TargetMethod() { return FeedBetterRidingCompatibility.GetMountStaminaSystemMethod("OnJumpStart"); } private static bool Prefix(object __instance) { return FeedBetterRidingCompatibility.OnJumpStart(__instance); } } [HarmonyPatch] internal static class BetterRidingMountAttackSafetyPatch { private static bool Prepare() { return FeedBetterRidingCompatibility.GetMountAttackUpdate() != null; } private static MethodBase? TargetMethod() { return FeedBetterRidingCompatibility.GetMountAttackUpdate(); } private static bool Prefix() { return FeedBetterRidingCompatibility.AllowMountAttackUpdate(); } } [HarmonyPatch] internal static class BetterRidingBlockFlyingDismountWaterPatch { private static bool Prepare() { return FeedBetterRidingCompatibility.GetBetterRidingBlockFlyingDismountPrefix() != null; } private static MethodBase? TargetMethod() { return FeedBetterRidingCompatibility.GetBetterRidingBlockFlyingDismountPrefix(); } [HarmonyPriority(800)] private static bool Prefix(Player __0, ref bool __result) { return FeedBetterRidingCompatibility.AllowBlockFlyingDismount(__0, ref __result); } } [HarmonyPatch] internal static class DragonRidersBlockFlyingDismountWaterPatch { private static bool Prepare() { return FeedBetterRidingCompatibility.GetDragonRidersBlockFlyingDismountPrefix() != null; } private static MethodBase? TargetMethod() { return FeedBetterRidingCompatibility.GetDragonRidersBlockFlyingDismountPrefix(); } [HarmonyPriority(800)] private static bool Prefix(Player __0, ref bool __result) { return FeedBetterRidingCompatibility.AllowBlockFlyingDismount(__0, ref __result); } } internal sealed class RawFeedConfig { public Dictionary Rules { get; } = new Dictionary(StringComparer.OrdinalIgnoreCase); public Dictionary FoodNumbers { get; } = new Dictionary(StringComparer.OrdinalIgnoreCase); public RawFeedValues Defaults { get; set; } = new RawFeedValues(); } internal sealed class RawFoodEntry { public string PrefabName { get; init; } = string.Empty; public int? RequiredQuality { get; init; } public float? Value { get; init; } public bool DefaultToOne { get; init; } } internal sealed class RawFeedValues { public float LevelUpConstant { get; init; } = 1f; public float TamingConstant { get; init; } = 1f; public float GrowthConstant { get; init; } = 1f; public float HealConstant { get; init; } = 1f; public float FedConstant { get; init; } = 60f; } internal sealed class RawTameValues { public float FedDuration { get; init; } public float TamingTime { get; init; } public bool StartsTamed { get; init; } public bool Commandable { get; init; } } internal sealed class RawProcreationValues { public int MaxCreatures { get; init; } public float TotalCheckRange { get; init; } } internal sealed class RawGrowValues { public float GrowTime { get; init; } } internal sealed class RawEggGrowValues { public float GrowTime { get; init; } } internal sealed class RawMountStaminaValues { public float AttackStaminaCost { get; init; } public float JumpStaminaCost { get; init; } public float AttackCooldown { get; init; } } internal sealed class RawFeedingRule { public string PrefabName { get; init; } = string.Empty; public RawFeedValues? Feed { get; set; } public RawTameValues? Tame { get; set; } public RawProcreationValues? Procreation { get; set; } public RawGrowValues? Grow { get; set; } public RawEggGrowValues? EggGrow { get; set; } public RawMountStaminaValues? MountStamina { get; set; } public bool HasRestrictedBiomeOverride { get; set; } public List RestrictedBiomes { get; } = new List(); public List Foods { get; } = new List(); public List ConsumeItems { get; } = new List(); public Dictionary> LevelUpItems { get; } = new Dictionary>(); } internal sealed class ResolvedFoodEntry { public string PrefabName { get; init; } = string.Empty; public int? RequiredQuality { get; init; } public float Value { get; init; } public Sprite Icon { get; init; } } internal sealed class ResolvedFeedingRule { public GameObject Prefab { get; init; } public float LevelUpConstant { get; init; } public float TamingConstant { get; init; } public float GrowthConstant { get; init; } public float HealConstant { get; init; } public float FedConstant { get; init; } public List Foods { get; init; } = new List(); public Dictionary> LevelUpItems { get; init; } = new Dictionary>(); public ResolvedMountStaminaValues? MountStamina { get; init; } public bool HasRestrictedBiomeOverride { get; init; } public IReadOnlyList RestrictedBiomes { get; init; } = Array.Empty(); } internal sealed class ResolvedMountStaminaValues { public float AttackStaminaCost { get; init; } public float JumpStaminaCost { get; init; } public float AttackCooldown { get; init; } } internal static class FeedingYamlParser { public static RawFeedConfig Parse(string yamlText, List warnings) { RawFeedConfig rawFeedConfig = new RawFeedConfig(); if (string.IsNullOrWhiteSpace(yamlText)) { return rawFeedConfig; } YamlStream yamlStream = new YamlStream(); yamlStream.Load(new StringReader(yamlText)); if (yamlStream.Documents.Count == 0 || !(yamlStream.Documents[0].RootNode is YamlMappingNode yamlMappingNode)) { warnings.Add("YAML root must be a mapping of defaults, foodNumbers, and prefab names."); return rawFeedConfig; } foreach (KeyValuePair child in yamlMappingNode.Children) { string text = ReadScalar(child.Key).Trim(); if (text.Length == 0) { warnings.Add(Location(child.Key) + ": top-level key cannot be empty."); continue; } if (text.Equals("defaults", StringComparison.OrdinalIgnoreCase)) { ParseDefaults(rawFeedConfig, child.Value, warnings); continue; } if (text.Equals("foodNumbers", StringComparison.OrdinalIgnoreCase)) { ParseFoodNumbers(rawFeedConfig.FoodNumbers, child.Value, "foodNumbers", warnings); continue; } RawFeedingRule value = ParseRule(text, child.Value, warnings); if (rawFeedConfig.Rules.ContainsKey(text)) { warnings.Add(Location(child.Key) + ": prefab '" + text + "' was defined more than once. The last definition won."); } rawFeedConfig.Rules[text] = value; } return rawFeedConfig; } private static void ParseDefaults(RawFeedConfig config, YamlNode node, List warnings) { if (!(node is YamlMappingNode yamlMappingNode)) { warnings.Add(Location(node) + ": defaults must be a mapping."); return; } foreach (KeyValuePair child in yamlMappingNode.Children) { string text = ReadScalar(child.Key).Trim(); if (text.Equals("feed", StringComparison.OrdinalIgnoreCase)) { config.Defaults = ParseFeedValues(child.Value, "defaults.feed", warnings); } else { warnings.Add(Location(child.Key) + ": unknown defaults field '" + text + "'."); } } } private static RawFeedingRule ParseRule(string prefabName, YamlNode node, List warnings) { RawFeedingRule rawFeedingRule = new RawFeedingRule { PrefabName = prefabName }; if (!(node is YamlMappingNode yamlMappingNode)) { warnings.Add(Location(node) + ": prefab '" + prefabName + "' must contain a mapping."); return rawFeedingRule; } foreach (KeyValuePair child in yamlMappingNode.Children) { string text = ReadScalar(child.Key).Trim(); switch (NormalizeField(text)) { case "feed": rawFeedingRule.Feed = ParseFeedValues(child.Value, prefabName + ".feed", warnings); break; case "consumeitems": ParseConsumeItems(rawFeedingRule, child.Value, warnings); break; case "levelupitems": ParseLevelUpItems(rawFeedingRule, child.Value, prefabName + ".levelUpItems", warnings); break; case "tame": rawFeedingRule.Tame = ParseTameValues(child.Value, prefabName + ".tame", warnings); break; case "procreation": rawFeedingRule.Procreation = ParseProcreationValues(child.Value, prefabName + ".procreation", warnings); break; case "grow": rawFeedingRule.Grow = ParseGrowValues(child.Value, prefabName + ".grow", warnings); break; case "egggrow": rawFeedingRule.EggGrow = ParseEggGrowValues(child.Value, prefabName + ".eggGrow", warnings); break; case "mountstamina": rawFeedingRule.MountStamina = ParseMountStaminaValues(child.Value, prefabName + ".mountStamina", warnings); break; case "restrictedbiome": case "restrictedbiomes": rawFeedingRule.HasRestrictedBiomeOverride = true; rawFeedingRule.RestrictedBiomes.Clear(); rawFeedingRule.RestrictedBiomes.AddRange((from value in ParseStringList(child.Value) select Unquote(value).Trim() into value where value.Length > 0 select value).Distinct(StringComparer.OrdinalIgnoreCase)); break; default: warnings.Add(Location(child.Key) + ": unknown field '" + text + "' for prefab '" + prefabName + "'."); break; } } return rawFeedingRule; } private static void ParseConsumeItems(RawFeedingRule rule, YamlNode node, List warnings) { if (node is YamlSequenceNode yamlSequenceNode) { { foreach (YamlNode child in yamlSequenceNode.Children) { AddConsumeItemEntry(rule, child, warnings); } return; } } foreach (string item in ParseStringList(node)) { AddConsumeItemEntry(rule, item, node, warnings); } } private static void AddConsumeItemEntry(RawFeedingRule rule, YamlNode node, List warnings) { if (node is YamlMappingNode yamlMappingNode && yamlMappingNode.Children.Count == 1) { KeyValuePair keyValuePair = yamlMappingNode.Children.First(); AddFoodEntry(rule, ReadScalar(keyValuePair.Key).Trim(), ReadScalar(keyValuePair.Value).Trim(), defaultToOne: true, node, warnings); AddConsumeItem(rule, ReadScalar(keyValuePair.Key).Trim(), node, warnings); return; } if (node is YamlSequenceNode yamlSequenceNode) { List list = yamlSequenceNode.Children.Select((YamlNode child) => ReadScalar(child).Trim()).ToList(); if (list.Count >= 2) { AddFoodEntry(rule, list[0], list[1], defaultToOne: true, node, warnings); AddConsumeItem(rule, list[0], node, warnings); return; } } AddConsumeItemEntry(rule, ReadScalar(node).Trim(), node, warnings); } private static void AddConsumeItemEntry(RawFeedingRule rule, string entryText, YamlNode node, List warnings) { string valueText = null; string foodName; if (TrySplitCommaValue(entryText, out string key, out string value)) { foodName = Unquote(key).Trim(); valueText = value; } else { foodName = Unquote(entryText).Trim(); } AddFoodEntry(rule, foodName, valueText, defaultToOne: true, node, warnings); AddConsumeItem(rule, foodName, node, warnings); } private static void AddFoodEntry(RawFeedingRule rule, string foodName, string? valueText, bool defaultToOne, YamlNode node, List warnings) { AddFoodEntry(rule.Foods, rule.PrefabName, foodName, valueText, defaultToOne, node, warnings); } private static void AddFoodEntry(List foods, string ruleName, string foodName, string? valueText, bool defaultToOne, YamlNode node, List warnings) { string prefabName; int? requiredQuality; if (string.IsNullOrWhiteSpace(foodName)) { warnings.Add(Location(node) + ": food item name cannot be empty."); } else if (TryParseFoodToken(foodName, node, warnings, out prefabName, out requiredQuality)) { float? value = null; if (!string.IsNullOrWhiteSpace(valueText)) { value = Math.Max(0f, ParseFloat(valueText, ruleName + "." + FormatFoodKey(prefabName, requiredQuality), node, warnings)); } int num = foods.FindIndex((RawFoodEntry entry) => entry.PrefabName.Equals(prefabName, StringComparison.OrdinalIgnoreCase) && entry.RequiredQuality == requiredQuality); if (num >= 0) { warnings.Add(Location(node) + ": food '" + FormatFoodKey(prefabName, requiredQuality) + "' was defined more than once for '" + ruleName + "'. The last value won."); foods.RemoveAt(num); } foods.Add(new RawFoodEntry { PrefabName = prefabName, RequiredQuality = requiredQuality, Value = value, DefaultToOne = defaultToOne }); } } private static void AddConsumeItem(RawFeedingRule rule, string foodName, YamlNode node, List warnings) { if (!string.IsNullOrWhiteSpace(foodName) && TryParseFoodToken(foodName, node, warnings, out string prefabName, out int? _) && rule.ConsumeItems.FindIndex((string entry) => entry.Equals(prefabName, StringComparison.OrdinalIgnoreCase)) < 0) { rule.ConsumeItems.Add(prefabName); } } private static void ParseLevelUpItems(RawFeedingRule rule, YamlNode node, string context, List warnings) { if (!(node is YamlMappingNode yamlMappingNode)) { warnings.Add(Location(node) + ": " + context + " must be a mapping of current Character level to consumeItems."); return; } foreach (KeyValuePair child in yamlMappingNode.Children) { int num = Math.Max(1, ParseInt(child.Key, context + ".level", warnings)); List list = new List(); if (child.Value is YamlSequenceNode yamlSequenceNode) { foreach (YamlNode child2 in yamlSequenceNode.Children) { AddLevelUpConsumeItemEntry(list, $"{rule.PrefabName}.levelUpItems.{num}", child2, warnings); } } else { foreach (string item in ParseStringList(child.Value)) { AddLevelUpConsumeItemEntry(list, $"{rule.PrefabName}.levelUpItems.{num}", item, child.Value, warnings); } } if (rule.LevelUpItems.ContainsKey(num)) { warnings.Add($"{Location(child.Key)}: levelUpItems for Character level {num} was defined more than once for prefab '{rule.PrefabName}'. The last definition won."); } rule.LevelUpItems[num] = list; } } private static void AddLevelUpConsumeItemEntry(List foods, string context, YamlNode node, List warnings) { if (node is YamlMappingNode yamlMappingNode && yamlMappingNode.Children.Count == 1) { KeyValuePair keyValuePair = yamlMappingNode.Children.First(); AddFoodEntry(foods, context, ReadScalar(keyValuePair.Key).Trim(), ReadScalar(keyValuePair.Value).Trim(), defaultToOne: true, node, warnings); return; } if (node is YamlSequenceNode yamlSequenceNode) { List list = yamlSequenceNode.Children.Select((YamlNode child) => ReadScalar(child).Trim()).ToList(); if (list.Count >= 2) { AddFoodEntry(foods, context, list[0], list[1], defaultToOne: true, node, warnings); return; } } AddLevelUpConsumeItemEntry(foods, context, ReadScalar(node).Trim(), node, warnings); } private static void AddLevelUpConsumeItemEntry(List foods, string context, string entryText, YamlNode node, List warnings) { string valueText = null; string foodName; if (TrySplitCommaValue(entryText, out string key, out string value)) { foodName = Unquote(key).Trim(); valueText = value; } else { foodName = Unquote(entryText).Trim(); } AddFoodEntry(foods, context, foodName, valueText, defaultToOne: true, node, warnings); } private static void ParseFoodNumbers(Dictionary foodNumbers, YamlNode node, string context, List warnings) { if (node is YamlSequenceNode yamlSequenceNode) { { foreach (YamlNode child in yamlSequenceNode.Children) { AddFoodNumber(foodNumbers, child, context, warnings); } return; } } warnings.Add(Location(node) + ": " + context + " must be a sequence. Use entries like '- Raspberry' or '- Raspberry, 3'."); } private static void AddFoodNumber(Dictionary foodNumbers, YamlNode node, string context, List warnings) { string text = ReadScalar(node).Trim(); string key; string value; bool flag = TrySplitCommaValue(text, out key, out value); if (TryParseFoodToken(Unquote(flag ? key : text).Trim(), node, warnings, out string prefabName, out int? requiredQuality)) { string text2 = FormatFoodKey(prefabName, requiredQuality); foodNumbers[text2] = (flag ? Math.Max(0f, ParseFloat(value, context + "." + text2, node, warnings)) : 1f); } } internal static string FormatFoodKey(string prefabName, int? requiredQuality) { if (!requiredQuality.HasValue) { return prefabName; } return prefabName + "@" + requiredQuality.Value.ToString(CultureInfo.InvariantCulture); } private static bool TryParseFoodToken(string foodName, YamlNode node, List warnings, out string prefabName, out int? requiredQuality) { prefabName = Unquote(foodName).Trim(); requiredQuality = null; if (prefabName.Length == 0) { warnings.Add(Location(node) + ": food item name cannot be empty."); return false; } int num = prefabName.LastIndexOf('@'); if (num < 0) { return true; } string text = prefabName.Substring(0, num).Trim(); string s = prefabName.Substring(num + 1).Trim(); if (text.Length == 0 || !int.TryParse(s, NumberStyles.Integer, CultureInfo.InvariantCulture, out var result) || result <= 0) { warnings.Add(Location(node) + ": food item '" + foodName + "' must use ItemName@quality with a positive integer quality."); prefabName = string.Empty; return false; } prefabName = text; requiredQuality = result; return true; } private static RawFeedValues ParseFeedValues(YamlNode node, string field, List warnings) { List list = ReadTupleParts(node); if (list.Count != 5) { warnings.Add(Location(node) + ": field '" + field + "' must contain 5 values: levelupconstant, tamingconstant, growthconstant, healconstant, fedconstant."); return new RawFeedValues(); } return new RawFeedValues { LevelUpConstant = Math.Max(0f, ParseFloat(list[0], field + ".levelupconstant", node, warnings)), TamingConstant = Math.Max(0f, ParseFloat(list[1], field + ".tamingconstant", node, warnings)), GrowthConstant = Math.Max(0f, ParseFloat(list[2], field + ".growthconstant", node, warnings)), HealConstant = Math.Max(0f, ParseFloat(list[3], field + ".healconstant", node, warnings)), FedConstant = Math.Max(0f, ParseFloat(list[4], field + ".fedconstant", node, warnings)) }; } private static RawTameValues? ParseTameValues(YamlNode node, string field, List warnings) { List list = ReadTupleParts(node); if (list.Count != 4) { warnings.Add(Location(node) + ": field '" + field + "' must contain 4 values: fedDuration, tamingTime, startsTamed, commandable."); return null; } return new RawTameValues { FedDuration = Math.Max(0f, ParseFloat(list[0], field + ".fedDuration", node, warnings)), TamingTime = Math.Max(0f, ParseFloat(list[1], field + ".tamingTime", node, warnings)), StartsTamed = ParseBool(list[2], field + ".startsTamed", node, warnings), Commandable = ParseBool(list[3], field + ".commandable", node, warnings) }; } private static RawProcreationValues? ParseProcreationValues(YamlNode node, string field, List warnings) { List list = ReadTupleParts(node); if (list.Count != 2) { warnings.Add(Location(node) + ": field '" + field + "' must contain 2 values: maxCreatures, totalCheckRange."); return null; } return new RawProcreationValues { MaxCreatures = Math.Max(0, ParseInt(list[0], field + ".maxCreatures", node, warnings)), TotalCheckRange = Math.Max(0f, ParseFloat(list[1], field + ".totalCheckRange", node, warnings)) }; } private static RawGrowValues? ParseGrowValues(YamlNode node, string field, List warnings) { List list = ReadTupleParts(node); if (list.Count != 1) { warnings.Add(Location(node) + ": field '" + field + "' must contain 1 value: growTime."); return null; } return new RawGrowValues { GrowTime = Math.Max(0f, ParseFloat(list[0], field + ".growTime", node, warnings)) }; } private static RawEggGrowValues? ParseEggGrowValues(YamlNode node, string field, List warnings) { List list = ReadTupleParts(node); if (list.Count != 1) { warnings.Add(Location(node) + ": field '" + field + "' must contain 1 value: growTime."); return null; } return new RawEggGrowValues { GrowTime = Math.Max(0f, ParseFloat(list[0], field + ".growTime", node, warnings)) }; } private static RawMountStaminaValues? ParseMountStaminaValues(YamlNode node, string field, List warnings) { List list = ReadTupleParts(node); if (list.Count != 3) { warnings.Add(Location(node) + ": field '" + field + "' must contain 3 values: attackStaminaCost, jumpStaminaCost, attackCooldown."); return null; } return new RawMountStaminaValues { AttackStaminaCost = Math.Max(0f, ParseFloat(list[0], field + ".attackStaminaCost", node, warnings)), JumpStaminaCost = Math.Max(0f, ParseFloat(list[1], field + ".jumpStaminaCost", node, warnings)), AttackCooldown = Math.Max(0f, ParseFloat(list[2], field + ".attackCooldown", node, warnings)) }; } private static List ParseStringList(YamlNode node) { if (node is YamlSequenceNode yamlSequenceNode) { return (from child in yamlSequenceNode.Children select ReadScalar(child).Trim() into value where value.Length > 0 select value).ToList(); } string text = ReadScalar(node).Trim(); if (text.Length == 0) { return new List(); } if (text.StartsWith("[", StringComparison.Ordinal) && text.EndsWith("]", StringComparison.Ordinal)) { return (from part in SplitCommaText(text.Substring(1, text.Length - 2)) select Unquote(part).Trim() into value where value.Length > 0 select value).ToList(); } return new List { Unquote(text) }; } private static List ReadTupleParts(YamlNode node) { if (node is YamlSequenceNode yamlSequenceNode) { return yamlSequenceNode.Children.Select((YamlNode child) => ReadScalar(child).Trim()).ToList(); } string text = ReadScalar(node).Trim(); if (text.StartsWith("[", StringComparison.Ordinal) && text.EndsWith("]", StringComparison.Ordinal)) { text = text.Substring(1, text.Length - 2); } return (from part in SplitCommaText(text) select Unquote(part).Trim()).ToList(); } private static int ParseInt(YamlNode node, string field, List warnings) { return ParseInt(ReadScalar(node), field, node, warnings); } private static int ParseInt(string valueText, string field, YamlNode node, List warnings) { if (int.TryParse(Unquote(valueText).Trim(), NumberStyles.Integer, CultureInfo.InvariantCulture, out var result)) { return result; } warnings.Add(Location(node) + ": field '" + field + "' must be an integer."); return 0; } private static float ParseFloat(YamlNode node, string field, List warnings) { return ParseFloat(ReadScalar(node), field, node, warnings); } private static float ParseFloat(string valueText, string field, YamlNode node, List warnings) { if (float.TryParse(Unquote(valueText).Trim(), NumberStyles.Float, CultureInfo.InvariantCulture, out var result)) { return result; } warnings.Add(Location(node) + ": field '" + field + "' must be a number."); return 0f; } private static bool ParseBool(string valueText, string field, YamlNode node, List warnings) { string text = Unquote(valueText).Trim(); if (bool.TryParse(text, out var result)) { return result; } string text2 = text.ToLowerInvariant(); if (text2 != null) { switch (text2.Length) { case 1: { char c = text2[0]; if ((uint)c <= 49u) { if (c != '0') { if (c != '1') { break; } goto IL_00dd; } } else if (c != 'n') { if (c != 'y') { break; } goto IL_00dd; } goto IL_00df; } case 3: { char c = text2[0]; if (c != 'o') { if (c != 'y' || !(text2 == "yes")) { break; } goto IL_00dd; } if (!(text2 == "off")) { break; } goto IL_00df; } case 2: { char c = text2[0]; if (c != 'n') { if (c != 'o' || !(text2 == "on")) { break; } goto IL_00dd; } if (!(text2 == "no")) { break; } goto IL_00df; } IL_00df: return false; IL_00dd: return true; } } warnings.Add(Location(node) + ": field '" + field + "' must be true or false."); return false; } private static string ReadScalar(YamlNode node) { object obj; if (!(node is YamlScalarNode yamlScalarNode)) { obj = node.ToString(); if (obj == null) { return string.Empty; } } else { obj = yamlScalarNode.Value ?? string.Empty; } return (string)obj; } private static string NormalizeField(string field) { return field.Replace("_", string.Empty).Replace("-", string.Empty).Trim() .ToLowerInvariant(); } private static bool TrySplitCommaValue(string text, out string key, out string value) { List list = SplitCommaText(text); if (list.Count < 2) { key = string.Empty; value = string.Empty; return false; } key = list[0].Trim(); value = list[1].Trim(); return key.Length > 0; } private static List SplitCommaText(string text) { List list = new List(); bool flag = false; bool flag2 = false; int num = 0; for (int i = 0; i < text.Length; i++) { char c = text[i]; if (c == '\'' && !flag2) { flag = !flag; } else if (c == '"' && !flag) { flag2 = !flag2; } else if (!(c != ',' || flag || flag2)) { list.Add(text.Substring(num, i - num).Trim()); num = i + 1; } } list.Add(text.Substring(num).Trim()); return list; } private static string Unquote(string text) { text = text.Trim(); if (text.Length >= 2 && ((text[0] == '"' && text[text.Length - 1] == '"') || (text[0] == '\'' && text[text.Length - 1] == '\''))) { return text.Substring(1, text.Length - 2).Replace("''", "'"); } return text; } private static string Location(YamlNode node) { if (node.Start.Line <= 0) { return "YAML"; } return $"Line {node.Start.Line}"; } } internal static class FeedDragonFlightRestrictionSystem { private sealed class RestrictionState { public int MountId { get; init; } public Biome Biome { get; init; } public bool Restricted { get; init; } } private const string FlightControlsTypeName = "BetterRiding.Behaviour.FlightControls"; private const string FlightControlsPlayerSetControlsPatchTypeName = "BetterRiding.Behaviour.FlightControls+Ygg_PlayerSetControls"; private const string MountControllerTypeName = "BetterRiding.Behaviour.MountController"; private const string MountControllerPlayerUpdatePatchTypeName = "BetterRiding.Behaviour.MountController+Player_Update_Patch"; private const string ExpandWorldBiomeManagerTypeName = "ExpandWorldData.BiomeManager"; private const string BiomeRestrictedMessage = "$flg_dragon_flight_restricted"; private const string EncumberedRestrictedMessage = "$flg_dragon_flight_encumbered"; private static readonly Dictionary PlayerStates = new Dictionary(); private static readonly Dictionary ResolvedBiomeNames = new Dictionary(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary TypeCache = new Dictionary(StringComparer.Ordinal); private static readonly Dictionary MethodCache = new Dictionary(StringComparer.Ordinal); private static readonly Dictionary FieldCache = new Dictionary(StringComparer.Ordinal); private static readonly Dictionary?> FlightKeyConfigCache = new Dictionary>(StringComparer.Ordinal); private static readonly Dictionary JoystickButtonNameCache = new Dictionary(); private static float _lastMessageTime = -100f; private static bool _isDragonDelegateSearched; private static Func? _isDragonDelegate; private static MethodInfo? _isDragonMethod; private static readonly object[] IsDragonArgs = new object[1]; private static string _cachedDefaultRestrictedBiomeText = string.Empty; private static IReadOnlyList _cachedDefaultRestrictedBiomes = Array.Empty(); internal static MethodBase? GetBetterRidingSetControlsPrefix() { return GetMethod("BetterRiding.Behaviour.FlightControls+Ygg_PlayerSetControls", "Prefix"); } internal static MethodBase? GetBetterRidingPlayerUpdatePostfix() { return GetMethod("BetterRiding.Behaviour.MountController+Player_Update_Patch", "Postfix"); } internal static void Shutdown() { PlayerStates.Clear(); ResolvedBiomeNames.Clear(); TypeCache.Clear(); MethodCache.Clear(); FieldCache.Clear(); FlightKeyConfigCache.Clear(); JoystickButtonNameCache.Clear(); _lastMessageTime = -100f; _isDragonDelegateSearched = false; _isDragonDelegate = null; _isDragonMethod = null; IsDragonArgs[0] = null; _cachedDefaultRestrictedBiomeText = string.Empty; _cachedDefaultRestrictedBiomes = Array.Empty(); } internal static bool BlockFlightToggleIfRestricted(Player player, ref bool jump) { //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) if (jump || !IsFlightToggleKeyDown()) { return true; } if (!TryGetRidingDragon(player, out Sadle _, out Character mount) || mount == null) { return true; } if (IsEncumberedFlightBlocked(player)) { CancelFlying(mount); ResetBetterRidingFlightKeyState(); ShowEncumberedRestrictedMessage(player); return false; } Biome freshBiome = GetFreshBiome(player, mount); if (!RefreshRestrictionState(player, freshBiome, cancelFlying: true, showWhenCanceled: false)) { return true; } CancelFlying(mount); ResetBetterRidingFlightKeyState(); ShowBiomeRestrictedMessage(player); return false; } internal static void CancelFlightToggleAfterSetControls(Player player) { //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) if (!IsFlightToggleKeyDown() || !TryGetRidingDragon(player, out Sadle _, out Character mount) || mount == null) { return; } if (IsEncumberedFlightBlocked(player)) { CancelFlying(mount); ResetBetterRidingFlightKeyState(); ShowEncumberedRestrictedMessage(player); return; } Biome freshBiome = GetFreshBiome(player, mount); if (RefreshRestrictionState(player, freshBiome, cancelFlying: true, showWhenCanceled: false)) { CancelFlying(mount); ResetBetterRidingFlightKeyState(); ShowBiomeRestrictedMessage(player); } } internal static void EnforceRestrictedFlightBeforeSaddleControls(Sadle saddle) { //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_005d: 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) Player localPlayer = Player.m_localPlayer; if (localPlayer == null || saddle == null || (object)localPlayer.m_doodadController != saddle || !IsBetterRidingDragon(saddle)) { return; } Character character = saddle.GetCharacter(); if (character != null) { int instanceId = GetInstanceId((Object?)(object)localPlayer); int instanceId2 = GetInstanceId((Object?)(object)character); Biome currentBiome = localPlayer.GetCurrentBiome(); RestrictionState value; bool flag = ((instanceId == 0 || !PlayerStates.TryGetValue(instanceId, out value) || value.MountId != instanceId2 || value.Biome != currentBiome) ? RefreshRestrictionState(localPlayer, currentBiome, cancelFlying: false, showWhenCanceled: false) : value.Restricted); if ((IsEncumberedFlightBlocked(localPlayer) || flag) && (character.m_flying || IsFlightToggleKeyHeld())) { CancelFlying(character); ResetBetterRidingFlightKeyState(); } } } internal static bool AllowBetterRidingPlayerUpdate(Player player) { if (TryGetRidingDragon(player, out Sadle saddle, out Character mount) && mount != null && IsEncumberedFlightBlocked(player)) { bool flying = mount.m_flying; bool flag = IsFlightToggleKeyHeld(); if (flying || flag) { CancelFlying(mount); ResetBetterRidingFlightKeyState(); ShowEncumberedRestrictedMessage(player); } return false; } int instanceId = GetInstanceId((Object?)(object)player); if (instanceId == 0 || !PlayerStates.TryGetValue(instanceId, out RestrictionState value) || !value.Restricted) { if (IsFlightToggleKeyHeld() && TryGetRestrictedRidingDragon(player, forceBiomeRefresh: true, out Character mount2, out Biome _)) { CancelFlying(mount2); ResetBetterRidingFlightKeyState(); ShowBiomeRestrictedMessage(player); return false; } return true; } if (!TryGetRidingDragon(player, out saddle, out Character mount3) || (Object)(object)mount3 == (Object)null) { PlayerStates.Remove(instanceId); return true; } bool flag2 = IsFlightToggleKeyHeld(); if (mount3.m_flying || flag2) { CancelFlying(mount3); ResetBetterRidingFlightKeyState(); if (flag2) { ShowBiomeRestrictedMessage(player); } } return false; } internal static void OnBiomeChanged(Player player, Biome biome) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) RefreshRestrictionState(player, biome, cancelFlying: true, showWhenCanceled: true); } internal static void OnStartDoodadControl(Player player) { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) if (!TryGetRidingDragon(player, out Sadle _, out Character mount) || mount == null) { ClearRestrictionState(player); return; } Biome freshBiome = GetFreshBiome(player, mount); RefreshRestrictionState(player, freshBiome, cancelFlying: true, showWhenCanceled: false); } internal static void ClearRestrictionState(Player player) { int instanceId = GetInstanceId((Object?)(object)player); if (instanceId != 0) { PlayerStates.Remove(instanceId); } } internal static void OnRulesChanged() { PlayerStates.Clear(); ResolvedBiomeNames.Clear(); Player localPlayer = Player.m_localPlayer; if (localPlayer != null && ((Character)localPlayer).IsRiding()) { OnStartDoodadControl(localPlayer); } } private static bool RefreshRestrictionState(Player player, Biome biome, bool cancelFlying, bool showWhenCanceled) { //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) int instanceId = GetInstanceId((Object?)(object)player); if (instanceId == 0) { return false; } if (!TryGetRidingDragon(player, out Sadle _, out Character mount) || mount == null) { PlayerStates.Remove(instanceId); return false; } bool flag = IsBiomeRestrictedForDragon(mount, biome); PlayerStates[instanceId] = new RestrictionState { MountId = GetInstanceId((Object?)(object)mount), Biome = biome, Restricted = flag }; if (flag && cancelFlying && mount.m_flying) { CancelFlying(mount); ResetBetterRidingFlightKeyState(); if (showWhenCanceled) { ShowBiomeRestrictedMessage(player); } } return flag; } private static bool TryGetRestrictedRidingDragon(Player player, bool forceBiomeRefresh, out Character? mount, out Biome biome) { //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Expected I4, but got Unknown mount = null; biome = (Biome)0; if (!TryGetRidingDragon(player, out Sadle _, out mount) || mount == null) { ClearRestrictionState(player); return false; } biome = (Biome)(int)(forceBiomeRefresh ? GetFreshBiome(player, mount) : player.GetCurrentBiome()); return RefreshRestrictionState(player, biome, cancelFlying: true, showWhenCanceled: false); } private static bool TryGetRidingDragon(Player player, out Sadle? saddle, out Character? mount) { saddle = null; mount = null; if (player == null || !((Character)player).IsRiding()) { return false; } IDoodadController doodadController = player.m_doodadController; saddle = (Sadle?)(object)((doodadController is Sadle) ? doodadController : null); if (saddle == null || !IsBetterRidingDragon(saddle)) { return false; } mount = saddle.GetCharacter(); return mount != null; } private static bool IsBetterRidingDragon(Sadle saddle) { if (!_isDragonDelegateSearched) { _isDragonMethod = GetMethod("BetterRiding.Behaviour.MountController", "isDragon", typeof(Sadle)); if (_isDragonMethod != null) { _isDragonDelegate = Delegate.CreateDelegate(typeof(Func), _isDragonMethod, throwOnBindFailure: false) as Func; } _isDragonDelegateSearched = true; } if (_isDragonDelegate != null) { return _isDragonDelegate(saddle); } if (_isDragonMethod == null) { return false; } try { IsDragonArgs[0] = saddle; object obj = _isDragonMethod.Invoke(null, IsDragonArgs); return obj is bool && (bool)obj; } catch (Exception ex) { FeedLikeGrandmaPlugin.Log.LogDebug((object)("BetterRiding dragon check failed: " + ex.Message)); return false; } finally { IsDragonArgs[0] = null; } } private static bool IsBiomeRestrictedForDragon(Character mount, Biome biome) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) IReadOnlyList restrictedBiomeNames = GetRestrictedBiomeNames(mount); if (restrictedBiomeNames.Count == 0) { return false; } return restrictedBiomeNames.Any((string name) => MatchesBiome(biome, name)); } private static IReadOnlyList GetRestrictedBiomeNames(Character mount) { if (FeedSystem.TryGetRestrictedBiomes(mount, out IReadOnlyList restrictedBiomes)) { return restrictedBiomes; } return GetDefaultRestrictedBiomeNames(); } private static IReadOnlyList GetDefaultRestrictedBiomeNames() { string text = FeedLikeGrandmaPlugin.DefaultDragonRestrictedBiomes.Value ?? string.Empty; if (string.Equals(_cachedDefaultRestrictedBiomeText, text, StringComparison.Ordinal)) { return _cachedDefaultRestrictedBiomes; } _cachedDefaultRestrictedBiomeText = text; _cachedDefaultRestrictedBiomes = SplitBiomeList(text); return _cachedDefaultRestrictedBiomes; } private static List SplitBiomeList(string text) { if (string.IsNullOrWhiteSpace(text)) { return new List(); } return (from part in text.Trim().Trim('[', ']').Split(new char[2] { ',', ';' }, StringSplitOptions.RemoveEmptyEntries) select part.Trim().Trim('\'', '"') into part where part.Length > 0 select part).Distinct(StringComparer.OrdinalIgnoreCase).ToList(); } private unsafe static bool MatchesBiome(Biome currentBiome, string restrictedName) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) if ((int)currentBiome == 0 || string.IsNullOrWhiteSpace(restrictedName)) { return false; } if (TryResolveBiomeName(restrictedName, out var biome) && (int)biome != 0 && (currentBiome == biome || (currentBiome & biome) != 0)) { return true; } string value = NormalizeBiomeName(restrictedName); if (NormalizeBiomeName(((object)(*(Biome*)(¤tBiome))/*cast due to .constrained prefix*/).ToString()).Equals(value, StringComparison.OrdinalIgnoreCase)) { return true; } if (TryGetExpandWorldBiomeDisplayName(currentBiome, out string displayName)) { return NormalizeBiomeName(displayName).Equals(value, StringComparison.OrdinalIgnoreCase); } return false; } private static bool TryResolveBiomeName(string name, out Biome biome) { //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Expected I4, but got Unknown biome = (Biome)0; string text = name.Trim(); if (text.Length == 0) { return false; } if (ResolvedBiomeNames.TryGetValue(text, out biome)) { return true; } if (Enum.TryParse(text, ignoreCase: true, out Biome result)) { biome = (Biome)(int)result; ResolvedBiomeNames[text] = biome; return true; } if (int.TryParse(text, out var result2)) { biome = (Biome)result2; ResolvedBiomeNames[text] = biome; return true; } if (TryGetExpandWorldBiome(text, out biome)) { ResolvedBiomeNames[text] = biome; return true; } return false; } private static bool TryGetExpandWorldBiome(string name, out Biome biome) { //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Expected I4, but got Unknown biome = (Biome)0; Type type = FindType("ExpandWorldData.BiomeManager"); MethodInfo methodInfo = ((type == null) ? null : AccessTools.Method(type, "TryGetBiome", new Type[2] { typeof(string), typeof(Biome).MakeByRefType() }, (Type[])null)); if (methodInfo == null) { return false; } object[] array = new object[2] { name, (object)(Biome)0 }; try { object obj = methodInfo.Invoke(null, array); if (obj is bool && (bool)obj) { biome = (Biome)(int)(Biome)array[1]; return true; } } catch (Exception ex) { FeedLikeGrandmaPlugin.Log.LogDebug((object)("Expand World Data biome lookup failed for '" + name + "': " + ex.Message)); } return false; } private static bool TryGetExpandWorldBiomeDisplayName(Biome biome, out string displayName) { //IL_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) displayName = string.Empty; Type type = FindType("ExpandWorldData.BiomeManager"); MethodInfo methodInfo = ((type == null) ? null : AccessTools.Method(type, "TryGetDisplayName", new Type[2] { typeof(Biome), typeof(string).MakeByRefType() }, (Type[])null)); if (methodInfo == null) { return false; } object[] array = new object[2] { biome, string.Empty }; try { object obj = methodInfo.Invoke(null, array); if (obj is bool && (bool)obj) { displayName = (array[1] as string) ?? string.Empty; return displayName.Length > 0; } } catch (Exception ex) { FeedLikeGrandmaPlugin.Log.LogDebug((object)$"Expand World Data biome display lookup failed for '{biome}': {ex.Message}"); } return false; } private static string NormalizeBiomeName(string name) { return new string(name.Where(char.IsLetterOrDigit).Select(char.ToLowerInvariant).ToArray()); } private static Biome GetFreshBiome(Player player, Character mount) { //IL_0006: 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) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) Biome val = Heightmap.FindBiome(((Component)mount).transform.position); if ((int)val != 0) { return val; } return player.GetCurrentBiome(); } private static void CancelFlying(Character? mount) { if (mount != null) { mount.m_flying = false; Animator componentInChildren = ((Component)mount).GetComponentInChildren(); if (componentInChildren != null) { componentInChildren.SetBool("IsAscending", false); componentInChildren.SetBool("IsDescending", false); } } } private static bool IsFlightToggleKeyDown() { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) if (!GetKeyDown(GetBetterRidingKey("FlightToggleKey"))) { return GetKeyDown(GetBetterRidingKey("FlightToggleKeyController")); } return true; } private static bool IsFlightToggleKeyHeld() { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) if (!GetKey(GetBetterRidingKey("FlightToggleKey")) && !GetKey(GetBetterRidingKey("FlightToggleKeyController"))) { return GetBetterRidingWasKeyPressed(); } return true; } private static KeyCode GetBetterRidingKey(string fieldName) { //IL_003f: Unknown result type (might be due to invalid IL or missing references) if (!FlightKeyConfigCache.TryGetValue(fieldName, out ConfigEntry value)) { value = GetField("BetterRiding.Behaviour.FlightControls", fieldName)?.GetValue(null) as ConfigEntry; FlightKeyConfigCache[fieldName] = value; } return (KeyCode)(((??)value?.Value) ?? 0); } private static bool GetKeyDown(KeyCode keyCode) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) if ((int)keyCode == 0) { return false; } if (IsJoystickButton(keyCode)) { return ZInput.GetButtonDown(GetJoystickButtonName(keyCode)); } if (!Input.GetKeyDown(keyCode)) { return ZInput.GetKeyDown(keyCode, true); } return true; } private static bool GetKey(KeyCode keyCode) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) if ((int)keyCode == 0) { return false; } if (IsJoystickButton(keyCode)) { return ZInput.GetButton(GetJoystickButtonName(keyCode)); } if (!Input.GetKey(keyCode)) { return ZInput.GetKey(keyCode, true); } return true; } private static bool IsJoystickButton(KeyCode keyCode) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Expected I4, but got Unknown int num = (int)keyCode; if (num >= 330) { return num <= 509; } return false; } private static string GetJoystickButtonName(KeyCode keyCode) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_00f2: Unknown result type (might be due to invalid IL or missing references) if (JoystickButtonNameCache.TryGetValue(keyCode, out string value)) { return value; } int buttonNumber = GetButtonNumber(keyCode); string text = ((buttonNumber < 0) ? "JoyJump" : (buttonNumber switch { 0 => "JoyJump", 1 => "JoyAttack", 2 => "JoyBlock", 3 => "JoyUse", 4 => "JoyLTrigger", 5 => "JoyRTrigger", 6 => "JoyMap", 7 => "JoyMenu", 8 => "JoyLStick", 9 => "JoyRStick", 10 => "JoyDPadLeft", 11 => "JoyDPadRight", 12 => "JoyDPadUp", 13 => "JoyDPadDown", _ => $"JoyButton{buttonNumber}", })); string text2 = text; JoystickButtonNameCache[keyCode] = text2; return text2; } private unsafe static int GetButtonNumber(KeyCode keyCode) { string text = ((object)(*(KeyCode*)(&keyCode))/*cast due to .constrained prefix*/).ToString(); int num = text.LastIndexOf("Button", StringComparison.OrdinalIgnoreCase); if (num < 0) { return -1; } if (!int.TryParse(text.Substring(num + 6), out var result)) { return -1; } return result; } private static bool GetBetterRidingWasKeyPressed() { object obj = GetBetterRidingWasKeyPressedField()?.GetValue(null); if (obj is bool) { return (bool)obj; } return false; } private static void ResetBetterRidingFlightKeyState() { GetBetterRidingWasKeyPressedField()?.SetValue(null, false); } private static FieldInfo? GetBetterRidingWasKeyPressedField() { return GetField("BetterRiding.Behaviour.MountController+Player_Update_Patch", "wasKeyPressed"); } private static bool IsEncumberedFlightBlocked(Player player) { if (FeedLikeGrandmaPlugin.BlockEncumberedDragonFlight.Value == FeedLikeGrandmaPlugin.Toggle.On && player != null) { return FeedRidingSystem.IsPlayerEncumberedCached(player); } return false; } private static void ShowBiomeRestrictedMessage(Player player) { ShowRestrictedMessage(player, "$flg_dragon_flight_restricted"); } private static void ShowEncumberedRestrictedMessage(Player player) { ShowRestrictedMessage(player, "$flg_dragon_flight_encumbered"); } private static void ShowRestrictedMessage(Player player, string message) { if (player != null && !(Time.time - _lastMessageTime < 1f)) { _lastMessageTime = Time.time; FeedLocalization.Message(player, message); } } private static int GetInstanceId(Object? obj) { if (obj != null) { return obj.GetInstanceID(); } return 0; } private static Type? FindType(string typeName) { if (TypeCache.TryGetValue(typeName, out Type value)) { return value; } Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); for (int i = 0; i < assemblies.Length; i++) { Type type = assemblies[i].GetType(typeName, throwOnError: false); if (type != null) { TypeCache[typeName] = type; return type; } } TypeCache[typeName] = null; return null; } private static MethodInfo? GetMethod(string typeName, string methodName, params Type[] parameterTypes) { string key = ((parameterTypes.Length == 0) ? (typeName + "." + methodName) : (typeName + "." + methodName + "(" + string.Join(",", parameterTypes.Select((Type type2) => type2.FullName)) + ")")); if (MethodCache.TryGetValue(key, out MethodInfo value)) { return value; } Type type = FindType(typeName); MethodInfo methodInfo = ((type == null) ? null : ((parameterTypes.Length == 0) ? AccessTools.Method(type, methodName, (Type[])null, (Type[])null) : AccessTools.Method(type, methodName, parameterTypes, (Type[])null))); MethodCache[key] = methodInfo; return methodInfo; } private static FieldInfo? GetField(string typeName, string fieldName) { string key = typeName + "." + fieldName; if (FieldCache.TryGetValue(key, out FieldInfo value)) { return value; } Type type = FindType(typeName); FieldInfo fieldInfo = ((type == null) ? null : AccessTools.Field(type, fieldName)); FieldCache[key] = fieldInfo; return fieldInfo; } } [HarmonyPatch] internal static class BetterRidingFlightToggleRestrictionPatch { private static bool Prepare() { return FeedDragonFlightRestrictionSystem.GetBetterRidingSetControlsPrefix() != null; } private static MethodBase? TargetMethod() { return FeedDragonFlightRestrictionSystem.GetBetterRidingSetControlsPrefix(); } [HarmonyPriority(800)] private static bool Prefix(Player __instance, ref bool jump) { return FeedDragonFlightRestrictionSystem.BlockFlightToggleIfRestricted(__instance, ref jump); } } [HarmonyPatch] internal static class BetterRidingPlayerUpdateFlightRestrictionPatch { private static bool Prepare() { return FeedDragonFlightRestrictionSystem.GetBetterRidingPlayerUpdatePostfix() != null; } private static MethodBase? TargetMethod() { return FeedDragonFlightRestrictionSystem.GetBetterRidingPlayerUpdatePostfix(); } [HarmonyPriority(800)] private static bool Prefix(Player __instance) { return FeedDragonFlightRestrictionSystem.AllowBetterRidingPlayerUpdate(__instance); } } [HarmonyPatch(typeof(Player), "SetControls")] internal static class PlayerSetControlsDragonFlightRestrictionPatch { private static bool Prepare() { return FeedBetterRidingCompatibility.HasBetterRiding; } [HarmonyPriority(0)] [HarmonyAfter(new string[] { "Yggdrah.BetterRiding" })] private static void Prefix(Player __instance) { FeedDragonFlightRestrictionSystem.CancelFlightToggleAfterSetControls(__instance); } } [HarmonyPatch(typeof(Sadle), "ApplyControlls")] internal static class SaddleApplyControlsDragonFlightRestrictionPatch { private static bool Prepare() { return FeedBetterRidingCompatibility.HasBetterRiding; } [HarmonyPriority(800)] [HarmonyBefore(new string[] { "Yggdrah.BetterRiding" })] private static void Prefix(Sadle __instance) { FeedDragonFlightRestrictionSystem.EnforceRestrictedFlightBeforeSaddleControls(__instance); } } [HarmonyPatch(typeof(Player), "AddKnownBiome")] internal static class PlayerAddKnownBiomeDragonFlightRestrictionPatch { private static bool Prepare() { return FeedBetterRidingCompatibility.HasBetterRiding; } private static void Postfix(Player __instance, Biome biome) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) FeedDragonFlightRestrictionSystem.OnBiomeChanged(__instance, biome); } } [HarmonyPatch(typeof(Player), "StartDoodadControl")] internal static class PlayerStartDoodadControlDragonFlightRestrictionPatch { private static bool Prepare() { return FeedBetterRidingCompatibility.HasBetterRiding; } private static void Postfix(Player __instance) { FeedDragonFlightRestrictionSystem.OnStartDoodadControl(__instance); } } [HarmonyPatch(typeof(Player), "StopDoodadControl")] internal static class PlayerStopDoodadControlDragonFlightRestrictionPatch { private static bool Prepare() { return FeedBetterRidingCompatibility.HasBetterRiding; } private static void Prefix(Player __instance) { FeedDragonFlightRestrictionSystem.ClearRestrictionState(__instance); } } internal static class FeedExternalReferenceSupport { private enum ProviderKind { CreatureManagerTemplate, JotunnCreatureManager } internal sealed class ExternalFeedProjection { public GameObject Prefab { get; init; } public string PrefabName { get; init; } = ""; public string OwnerName { get; init; } = ""; public List ConsumeItemNames { get; init; } = new List(); } private sealed class ProviderHandle { public ProviderKind Kind { get; init; } public string AssemblyName { get; init; } = ""; public string OwnerName { get; init; } = ""; public PropertyInfo? ManagerInstanceProperty { get; init; } public FieldInfo? ManagerCreaturesField { get; init; } public PropertyInfo? ManagerCreaturesProperty { get; init; } public FieldInfo? RegisteredCreaturesField { get; init; } public FieldInfo? CreatureConfigsField { get; init; } } private sealed class ProjectionSnapshot { public List Projections { get; init; } = new List(); public Dictionary ByPrefabName { get; init; } = new Dictionary(StringComparer.OrdinalIgnoreCase); } private static readonly object Sync = new object(); private static string _providerAssemblySignature = ""; private static List _providerCache = new List(); private static string _snapshotAssemblySignature = ""; private static string _snapshotDataSignature = ""; private static ProjectionSnapshot? _snapshotCache; internal static void RefreshSnapshot() { Assembly[] assemblies = (from assembly in AppDomain.CurrentDomain.GetAssemblies() where !assembly.IsDynamic select assembly).ToArray(); string text = BuildAssemblySignature(assemblies); lock (Sync) { List list = (from projection in GetProviders(assemblies, text).SelectMany(CollectProjections) where projection.Prefab != null && !string.IsNullOrWhiteSpace(projection.PrefabName) select projection).ToList(); string text2 = BuildProjectionSignature(list); if (_snapshotCache != null && string.Equals(_snapshotAssemblySignature, text, StringComparison.Ordinal) && string.Equals(_snapshotDataSignature, text2, StringComparison.Ordinal)) { return; } Dictionary dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); foreach (ExternalFeedProjection item in list) { if (!dictionary.TryGetValue(item.PrefabName, out var value)) { dictionary[item.PrefabName] = item; continue; } List consumeItemNames = (from name in value.ConsumeItemNames.Concat(item.ConsumeItemNames) where !string.IsNullOrWhiteSpace(name) select name).Distinct(StringComparer.OrdinalIgnoreCase).OrderBy((string name) => name, StringComparer.OrdinalIgnoreCase).ToList(); dictionary[item.PrefabName] = new ExternalFeedProjection { Prefab = value.Prefab, PrefabName = value.PrefabName, OwnerName = (string.IsNullOrWhiteSpace(value.OwnerName) ? item.OwnerName : value.OwnerName), ConsumeItemNames = consumeItemNames }; } _snapshotAssemblySignature = text; _snapshotDataSignature = text2; _snapshotCache = new ProjectionSnapshot { Projections = dictionary.Values.OrderBy((ExternalFeedProjection projection) => projection.PrefabName, StringComparer.OrdinalIgnoreCase).ToList(), ByPrefabName = dictionary }; } } internal static void Shutdown() { lock (Sync) { _providerAssemblySignature = ""; _providerCache.Clear(); _snapshotAssemblySignature = ""; _snapshotDataSignature = ""; _snapshotCache = null; } } internal static IEnumerable GetExternalPrefabs() { return from projection in GetSnapshot().Projections select projection.Prefab into prefab where (Object)(object)prefab != (Object)null select prefab; } internal static List GetConfiguredConsumeItemNames(GameObject prefab) { if (prefab == null || string.IsNullOrWhiteSpace(((Object)prefab).name)) { return new List(); } if (!GetSnapshot().ByPrefabName.TryGetValue(((Object)prefab).name, out ExternalFeedProjection value)) { return new List(); } return value.ConsumeItemNames.ToList(); } internal static List ResolveConfiguredConsumeItems(GameObject prefab) { List list = new List(); if (ObjectDB.instance == null) { return list; } foreach (string configuredConsumeItemName in GetConfiguredConsumeItemNames(prefab)) { GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(configuredConsumeItemName); ItemDrop val = (((Object)(object)itemPrefab == (Object)null) ? null : itemPrefab.GetComponent()); if (val != null) { list.Add(val); } } return list; } internal static bool TryGetOwnerName(string prefabName, out string ownerName) { ownerName = ""; if (string.IsNullOrWhiteSpace(prefabName)) { return false; } if (!GetSnapshot().ByPrefabName.TryGetValue(prefabName, out ExternalFeedProjection value) || string.IsNullOrWhiteSpace(value.OwnerName)) { return false; } ownerName = value.OwnerName; return true; } internal static string ExtractItemDropPrefabName(ItemDrop? itemDrop) { if (itemDrop == null) { return ""; } GameObject val = itemDrop.m_itemData?.m_dropPrefab; if (val != null && !string.IsNullOrWhiteSpace(((Object)val).name)) { return NormalizePrefabName(((Object)val).name); } return NormalizePrefabName(((Object)((Component)itemDrop).gameObject).name); } private static ProjectionSnapshot GetSnapshot() { if (_snapshotCache == null) { RefreshSnapshot(); } return _snapshotCache ?? new ProjectionSnapshot(); } private static List GetProviders(Assembly[] assemblies, string assemblySignature) { if (string.Equals(_providerAssemblySignature, assemblySignature, StringComparison.Ordinal)) { return _providerCache; } List list = new List(); foreach (Assembly assembly in assemblies) { Type type = SafeGetType(assembly, "CreatureManager.Creature"); if (!(type == null)) { FieldInfo fieldInfo = TryGetTypeField(type, "registeredCreatures"); if (!(fieldInfo == null)) { list.Add(new ProviderHandle { Kind = ProviderKind.CreatureManagerTemplate, AssemblyName = (assembly.GetName().Name ?? assembly.FullName ?? "Unknown / Untracked"), OwnerName = ResolveAssemblyOwnerName(assembly), RegisteredCreaturesField = fieldInfo, CreatureConfigsField = TryGetTypeField(type, "creatureConfigs") }); } } } ProviderHandle providerHandle = TryCreateJotunnCreatureProvider(assemblies); if (providerHandle != null) { list.Add(providerHandle); } _providerAssemblySignature = assemblySignature; _providerCache = list; return _providerCache; } private static ProviderHandle? TryCreateJotunnCreatureProvider(IEnumerable assemblies) { foreach (Assembly assembly in assemblies) { Type type = SafeGetType(assembly, "Jotunn.Managers.CreatureManager"); Type type2 = SafeGetType(assembly, "Jotunn.Entities.CustomCreature"); if (!(type == null) && !(type2 == null)) { PropertyInfo propertyInfo = TryGetTypeProperty(type, "Instance"); FieldInfo fieldInfo = TryGetTypeField(type, "Creatures"); PropertyInfo propertyInfo2 = TryGetTypeProperty(type, "Creatures"); if (!(propertyInfo == null) && (!(fieldInfo == null) || !(propertyInfo2 == null))) { return new ProviderHandle { Kind = ProviderKind.JotunnCreatureManager, AssemblyName = (assembly.GetName().Name ?? assembly.FullName ?? "Jotunn"), OwnerName = ResolveAssemblyOwnerName(assembly), ManagerInstanceProperty = propertyInfo, ManagerCreaturesField = fieldInfo, ManagerCreaturesProperty = propertyInfo2 }; } } } return null; } private static IEnumerable CollectProjections(ProviderHandle provider) { if (provider.Kind != ProviderKind.JotunnCreatureManager) { return CollectCreatureManagerTemplateProjections(provider); } return CollectJotunnCreatureProjections(provider); } private static IEnumerable CollectJotunnCreatureProjections(ProviderHandle provider) { object obj = provider.ManagerInstanceProperty?.GetValue(null, null); if (obj == null) { yield break; } object value = provider.ManagerCreaturesField?.GetValue(obj) ?? provider.ManagerCreaturesProperty?.GetValue(obj, null); foreach (object item in GetEnumerable(value)) { if (item != null && TryGetRawMemberValue(item, "Prefab", out object value2)) { GameObject val = (GameObject)((value2 is GameObject) ? value2 : null); if (val != null && !string.IsNullOrWhiteSpace(((Object)val).name)) { yield return new ExternalFeedProjection { Prefab = val, PrefabName = NormalizePrefabName(((Object)val).name), OwnerName = ResolveJotunnOwnerName(item, provider.OwnerName), ConsumeItemNames = ExtractConsumeItemNames(val) }; } } } } private static IEnumerable CollectCreatureManagerTemplateProjections(ProviderHandle provider) { IEnumerable enumerable = GetEnumerable(provider.RegisteredCreaturesField?.GetValue(null)); IDictionary creatureConfigs = provider.CreatureConfigsField?.GetValue(null) as IDictionary; foreach (object item in enumerable) { if (item == null || !TryGetRawMemberValue(item, "Prefab", out object value)) { continue; } GameObject val = (GameObject)((value is GameObject) ? value : null); if (val != null && !string.IsNullOrWhiteSpace(((Object)val).name)) { object creatureConfig = null; if (creatureConfigs != null && creatureConfigs.Contains(item)) { creatureConfig = creatureConfigs[item]; } List list = ReadCreatureManagerConsumeNames(item, creatureConfig); if (list.Count == 0) { list = ExtractConsumeItemNames(val); } yield return new ExternalFeedProjection { Prefab = val, PrefabName = NormalizePrefabName(((Object)val).name), OwnerName = (string.IsNullOrWhiteSpace(provider.OwnerName) ? provider.AssemblyName : provider.OwnerName), ConsumeItemNames = list }; } } } private static List ReadCreatureManagerConsumeNames(object creature, object? creatureConfig) { if (TryGetWrappedMemberValue(creatureConfig, "ConsumesItemName", out object value) && value is string value2) { List list = SplitConsumeItemNames(value2); if (list.Count > 0) { return list; } } if (TryGetWrappedMemberValue(creature, "ConsumesItemName", out object value3) && value3 is string value4) { List list2 = SplitConsumeItemNames(value4); if (list2.Count > 0) { return list2; } } if (TryGetRawMemberValue(creature, "FoodItems", out object value5)) { return SplitConsumeItemNames(value5?.ToString()); } return new List(); } private static List ExtractConsumeItemNames(GameObject prefab) { MonsterAI component = prefab.GetComponent(); if (component == null || component.m_consumeItems == null) { return new List(); } return (from name in component.m_consumeItems.Where((ItemDrop item) => item != null).Select(ExtractItemDropPrefabName) where !string.IsNullOrWhiteSpace(name) select name).Distinct(StringComparer.OrdinalIgnoreCase).OrderBy((string name) => name, StringComparer.OrdinalIgnoreCase).ToList(); } private static List SplitConsumeItemNames(string? value) { if (string.IsNullOrWhiteSpace(value)) { return new List(); } return (from name in value.Split(new char[1] { ',' }).Select(NormalizePrefabName) where !string.IsNullOrWhiteSpace(name) select name).Distinct(StringComparer.OrdinalIgnoreCase).OrderBy((string name) => name, StringComparer.OrdinalIgnoreCase).ToList(); } private static string ResolveJotunnOwnerName(object creature, string fallback) { if (TryGetRawMemberValue(creature, "SourceMod", out object value) && value != null) { if (TryGetRawMemberValue(value, "GUID", out object value2)) { string text = NormalizePrefabName(value2?.ToString()); if (text.Length > 0 && Chainloader.PluginInfos.TryGetValue(text, out var value3)) { string text2 = NormalizePrefabName(value3.Metadata.Name); if (text2.Length <= 0) { return text; } return text2; } if (text.Length > 0) { return text; } } if (TryGetRawMemberValue(value, "Name", out object value4)) { string text3 = NormalizePrefabName(value4?.ToString()); if (text3.Length > 0) { return text3; } } } return fallback; } private static string ResolveAssemblyOwnerName(Assembly assembly) { PluginInfo val = ((IEnumerable)Chainloader.PluginInfos.Values).FirstOrDefault((Func)((PluginInfo info) => (Object)(object)info.Instance != (Object)null && ((object)info.Instance).GetType().Assembly == assembly)); if (val != null) { string text = NormalizePrefabName(val.Metadata.Name); if (text.Length > 0) { return text; } string text2 = NormalizePrefabName(val.Metadata.GUID); if (text2.Length > 0) { return text2; } } return assembly.GetName().Name ?? "Unknown / Untracked"; } private static bool TryGetWrappedMemberValue(object? instance, string memberName, out object? value) { value = null; if (!TryGetRawMemberValue(instance, memberName, out object value2)) { return false; } value = UnwrapConfigValue(value2); return true; } private static bool TryGetRawMemberValue(object? instance, string memberName, out object? value) { value = null; if (instance == null || string.IsNullOrWhiteSpace(memberName)) { return false; } Type type = instance.GetType(); FieldInfo fieldInfo = type.GetField(memberName, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic) ?? type.GetFields(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault((FieldInfo candidate) => string.Equals(candidate.Name, memberName, StringComparison.OrdinalIgnoreCase)); if (fieldInfo != null) { value = fieldInfo.GetValue(instance); return true; } PropertyInfo propertyInfo = type.GetProperty(memberName, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic) ?? type.GetProperties(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault((PropertyInfo candidate) => string.Equals(candidate.Name, memberName, StringComparison.OrdinalIgnoreCase)); if (propertyInfo == null || !propertyInfo.CanRead) { return false; } value = propertyInfo.GetValue(instance, null); return true; } private static object? UnwrapConfigValue(object? value) { object obj = value; for (int i = 0; i < 5; i++) { if (obj == null) { break; } if (obj is string || obj.GetType().IsPrimitive || obj.GetType().IsEnum) { return obj; } ConfigEntryBase val = (ConfigEntryBase)((obj is ConfigEntryBase) ? obj : null); if (val != null) { return val.BoxedValue; } if (obj is Delegate obj2) { try { obj = obj2.DynamicInvoke(); } catch { break; } continue; } if (TryGetRawMemberValue(obj, "config", out object value2) && value2 != null) { obj = value2; continue; } if (TryGetRawMemberValue(obj, "get", out object value3) && value3 is Delegate obj4) { try { obj = obj4.DynamicInvoke(); } catch { break; } continue; } MethodInfo method = obj.GetType().GetMethod("get", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null); if (method != null && method.ReturnType != typeof(void)) { obj = method.Invoke(obj, null); continue; } PropertyInfo property = obj.GetType().GetProperty("BoxedValue", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (property != null && property.CanRead) { obj = property.GetValue(obj, null); continue; } PropertyInfo property2 = obj.GetType().GetProperty("Value", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (!(property2 != null) || !property2.CanRead) { break; } obj = property2.GetValue(obj, null); } return obj; } private static IEnumerable GetEnumerable(object? value) { if (value is string || !(value is IEnumerable result)) { return Array.Empty(); } return result; } private static FieldInfo? TryGetTypeField(Type type, string fieldName) { return type.GetField(fieldName, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic) ?? type.GetFields(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault((FieldInfo field) => string.Equals(field.Name, fieldName, StringComparison.OrdinalIgnoreCase)); } private static PropertyInfo? TryGetTypeProperty(Type type, string propertyName) { return type.GetProperty(propertyName, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic) ?? type.GetProperties(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault((PropertyInfo property) => string.Equals(property.Name, propertyName, StringComparison.OrdinalIgnoreCase)); } private static Type? SafeGetType(Assembly assembly, string typeName) { try { return assembly.GetType(typeName, throwOnError: false); } catch { return null; } } private static string BuildAssemblySignature(IEnumerable assemblies) { return BuildStableHash(from assembly in assemblies select assembly.FullName ?? assembly.GetName().Name ?? "" into token where token.Length > 0 select token); } private static string BuildProjectionSignature(IEnumerable projections) { return BuildStableHash(projections.Select(delegate(ExternalFeedProjection projection) { int num = ((!((Object)(object)projection.Prefab == (Object)null)) ? ((Object)projection.Prefab).GetInstanceID() : 0); return string.Format("{0}|{1}|{2}|{3}", projection.PrefabName, projection.OwnerName, num, string.Join(",", projection.ConsumeItemNames)); })); } private static string BuildStableHash(IEnumerable tokens) { int num = 17; foreach (string item in tokens.OrderBy((string token) => token, StringComparer.Ordinal)) { num = num * 31 + StringComparer.Ordinal.GetHashCode(item); } return num.ToString("x8"); } private static string NormalizePrefabName(string? name) { string text = (name ?? "").Replace("(Clone)", "").Trim(); bool flag = false; if (text.StartsWith("JVLmock_", StringComparison.Ordinal)) { text = text.Substring("JVLmock_".Length); flag = true; } else if (text.StartsWith("VLmock_", StringComparison.Ordinal)) { text = text.Substring("VLmock_".Length); flag = true; } int num = text.IndexOf("__", StringComparison.Ordinal); if (flag && num > 0) { text = text.Substring(0, num); } return text.Trim(); } } internal static class FeedMonstrumCompatibility { internal enum Package { Monstrum, MonstrumDeepNorth } private const string MonstrumUpdateDoodadControlsPatchTypeName = "Monstrum.TwPlayerUpdateDoodadControlsPatch"; private const string MonstrumDeepNorthUpdateDoodadControlsPatchTypeName = "MonstrumDeepNorth.TwPlayerUpdateDoodadControlsPatch"; private const string MonstrumPlayerExtensionsTypeName = "Monstrum.PlayerExtensions"; private const string MonstrumDeepNorthPlayerExtensionsTypeName = "MonstrumDeepNorth.PlayerExtensions"; private const string MonstrumRidingPatchTypeName = "Monstrum.StartRidingMountPatch_Monstrum"; private const string MonstrumDeepNorthRidingPatchTypeName = "MonstrumDeepNorth.StartRidingMountPatch_MonstrumDeepNorth"; private static readonly HashSet MonstrumMountPrefabs = new HashSet { "Razorback_TW", "BlackBear_TW", "GrizzlyBear_TW", "Prowler_TW" }; private static readonly HashSet MonstrumDeepNorthMountPrefabs = new HashSet { "ArcticMammoth_TW", "ArcticBear_TW" }; private static readonly Dictionary TypeCache = new Dictionary(StringComparer.Ordinal); private static readonly Dictionary MethodCache = new Dictionary(StringComparer.Ordinal); private static readonly Dictionary FieldCache = new Dictionary(StringComparer.Ordinal); internal static MethodBase? GetUpdateDoodadControlsPostfix(Package package) { return GetMethod((package == Package.Monstrum) ? "Monstrum.TwPlayerUpdateDoodadControlsPatch" : "MonstrumDeepNorth.TwPlayerUpdateDoodadControlsPatch", "Postfix"); } internal static MethodBase? GetCustomAttachStop(Package package) { return GetMethod((package == Package.Monstrum) ? "Monstrum.PlayerExtensions" : "MonstrumDeepNorth.PlayerExtensions", "CustomAttachStop", typeof(Player)); } private static Type? FindType(string typeName) { if (TypeCache.TryGetValue(typeName, out Type value)) { return value; } Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); for (int i = 0; i < assemblies.Length; i++) { Type type = assemblies[i].GetType(typeName, throwOnError: false); if (type != null) { TypeCache[typeName] = type; return type; } } TypeCache[typeName] = null; return null; } private static MethodInfo? GetMethod(string typeName, string methodName, params Type[] parameterTypes) { string key = ((parameterTypes.Length == 0) ? (typeName + "." + methodName) : (typeName + "." + methodName + "(" + string.Join(",", Array.ConvertAll(parameterTypes, (Type type2) => type2.FullName)) + ")")); if (MethodCache.TryGetValue(key, out MethodInfo value)) { return value; } Type type = FindType(typeName); MethodInfo methodInfo = ((type == null) ? null : ((parameterTypes.Length == 0) ? AccessTools.Method(type, methodName, (Type[])null, (Type[])null) : AccessTools.Method(type, methodName, parameterTypes, (Type[])null))); MethodCache[key] = methodInfo; return methodInfo; } private static FieldInfo? GetField(string typeName, string fieldName) { string key = typeName + "." + fieldName; if (FieldCache.TryGetValue(key, out FieldInfo value)) { return value; } Type type = FindType(typeName); FieldInfo fieldInfo = ((type == null) ? null : AccessTools.Field(type, fieldName)); FieldCache[key] = fieldInfo; return fieldInfo; } internal static bool ShouldSkipJumpDismountPostfix(Player player, Package package) { if (!IsEnabled() || !HasJumpInput() || !TryGetRidingMonstrumMount(player, package, out Character mount)) { return false; } if (mount == null) { return false; } return mount.GetHealth() > 0f; } internal static void ConvertJumpToMountJump(Player player, ref bool jump) { if (jump && IsEnabled() && TryGetRidingMonstrumMount(player, out Character mount) && mount != null && !(mount.GetHealth() <= 0f)) { mount.Jump(false); jump = false; } } internal static bool ShouldAllowCustomAttachStop(Player player, Package package) { if (!IsEnabled() || !HasJumpInput() || !TryGetRidingMonstrumMount(player, package, out Character mount)) { return true; } if (mount != null) { return mount.GetHealth() <= 0f; } return true; } private static bool IsEnabled() { return FeedLikeGrandmaPlugin.MonstrumMountJumpCompatibility.Value.IsOn(); } private static bool HasJumpInput() { if (!ZInput.GetButton("Jump")) { return ZInput.GetButtonDown("JoyJump"); } return true; } private static bool TryGetRidingMonstrumMount(Player player, out Character? mount) { mount = null; if (TryGetPackageRidingMount(Package.Monstrum, out mount) || TryGetPackageRidingMount(Package.MonstrumDeepNorth, out mount)) { return true; } if (!TryGetRidingSaddle(player, out Sadle saddle, out string prefabName)) { return false; } if (!IsMonstrumMountPrefab(prefabName)) { return false; } mount = saddle.GetCharacter(); return mount != null; } private static bool TryGetRidingMonstrumMount(Player player, Package package, out Character? mount) { mount = null; if (TryGetPackageRidingMount(package, out mount)) { return true; } if (!TryGetRidingSaddle(player, out Sadle saddle, out string prefabName)) { return false; } if (!IsMonstrumMountPrefab(prefabName, package)) { return false; } mount = saddle.GetCharacter(); return mount != null; } private static bool TryGetRidingSaddle(Player player, out Sadle? saddle, out string prefabName) { saddle = null; prefabName = string.Empty; if (player == null || !((Character)player).IsRiding()) { return false; } IDoodadController doodadController = player.m_doodadController; saddle = (Sadle?)(object)((doodadController is Sadle) ? doodadController : null); Sadle? obj = saddle; Component val = ((obj != null) ? obj.GetControlledComponent() : null); if (val == null) { return false; } prefabName = Utils.GetPrefabName(((Object)val.gameObject).name); return !string.IsNullOrWhiteSpace(prefabName); } private static bool TryGetPackageRidingMount(Package package, out Character? mount) { mount = null; string typeName = ((package == Package.Monstrum) ? "Monstrum.StartRidingMountPatch_Monstrum" : "MonstrumDeepNorth.StartRidingMountPatch_MonstrumDeepNorth"); string fieldName = ((package == Package.Monstrum) ? "RidingMountMonstrum" : "RidingMountMonstrumDeepNorth"); FieldInfo field = GetField(typeName, fieldName); FieldInfo field2 = GetField(typeName, "RidingHumanoid"); object obj = field?.GetValue(null); if (obj is bool && (bool)obj) { object? obj2 = field2?.GetValue(null); Humanoid val = (Humanoid)((obj2 is Humanoid) ? obj2 : null); if (val != null) { mount = (Character?)(object)val; return mount != null; } } return false; } private static bool IsMonstrumMountPrefab(string prefabName) { if (!MonstrumMountPrefabs.Contains(prefabName)) { return MonstrumDeepNorthMountPrefabs.Contains(prefabName); } return true; } private static bool IsMonstrumMountPrefab(string prefabName, Package package) { if (package != Package.Monstrum) { return MonstrumDeepNorthMountPrefabs.Contains(prefabName); } return MonstrumMountPrefabs.Contains(prefabName); } } [HarmonyPatch(typeof(Player), "SetControls")] internal static class PlayerSetControlsMonstrumMountJumpPatch { [HarmonyPriority(800)] [HarmonyBefore(new string[] { "Yggdrah.BetterRiding" })] private static void Prefix(Player __instance, ref bool jump) { FeedMonstrumCompatibility.ConvertJumpToMountJump(__instance, ref jump); } } [HarmonyPatch] internal static class MonstrumUpdateDoodadControlsCompatibilityPatch { private static bool Prepare() { return FeedMonstrumCompatibility.GetUpdateDoodadControlsPostfix(FeedMonstrumCompatibility.Package.Monstrum) != null; } private static MethodBase? TargetMethod() { return FeedMonstrumCompatibility.GetUpdateDoodadControlsPostfix(FeedMonstrumCompatibility.Package.Monstrum); } private static bool Prefix(Player __instance) { return !FeedMonstrumCompatibility.ShouldSkipJumpDismountPostfix(__instance, FeedMonstrumCompatibility.Package.Monstrum); } } [HarmonyPatch] internal static class MonstrumCustomAttachStopCompatibilityPatch { private static bool Prepare() { return FeedMonstrumCompatibility.GetCustomAttachStop(FeedMonstrumCompatibility.Package.Monstrum) != null; } private static MethodBase? TargetMethod() { return FeedMonstrumCompatibility.GetCustomAttachStop(FeedMonstrumCompatibility.Package.Monstrum); } private static bool Prefix(Player __0) { return FeedMonstrumCompatibility.ShouldAllowCustomAttachStop(__0, FeedMonstrumCompatibility.Package.Monstrum); } } [HarmonyPatch] internal static class MonstrumDeepNorthUpdateDoodadControlsCompatibilityPatch { private static bool Prepare() { return FeedMonstrumCompatibility.GetUpdateDoodadControlsPostfix(FeedMonstrumCompatibility.Package.MonstrumDeepNorth) != null; } private static MethodBase? TargetMethod() { return FeedMonstrumCompatibility.GetUpdateDoodadControlsPostfix(FeedMonstrumCompatibility.Package.MonstrumDeepNorth); } private static bool Prefix(Player __instance) { return !FeedMonstrumCompatibility.ShouldSkipJumpDismountPostfix(__instance, FeedMonstrumCompatibility.Package.MonstrumDeepNorth); } } [HarmonyPatch] internal static class MonstrumDeepNorthCustomAttachStopCompatibilityPatch { private static bool Prepare() { return FeedMonstrumCompatibility.GetCustomAttachStop(FeedMonstrumCompatibility.Package.MonstrumDeepNorth) != null; } private static MethodBase? TargetMethod() { return FeedMonstrumCompatibility.GetCustomAttachStop(FeedMonstrumCompatibility.Package.MonstrumDeepNorth); } private static bool Prefix(Player __0) { return FeedMonstrumCompatibility.ShouldAllowCustomAttachStop(__0, FeedMonstrumCompatibility.Package.MonstrumDeepNorth); } } [HarmonyPatch(typeof(ObjectDB), "Awake")] internal static class ObjectDbAwakePatch { private static void Postfix(ObjectDB __instance) { TamingOrbSystem.OnGameDataChanged(); FeedBarrelSystem.OnGameDataChanged(); FeedLikeGrandmaPlugin.QueueRuntimeRefresh(RuntimeRefreshFlags.All); } } [HarmonyPatch(typeof(ObjectDB), "CopyOtherDB")] internal static class ObjectDbCopyPatch { private static void Postfix(ObjectDB __instance) { TamingOrbSystem.OnGameDataChanged(); FeedBarrelSystem.OnGameDataChanged(); FeedLikeGrandmaPlugin.QueueRuntimeRefresh(RuntimeRefreshFlags.All); } } [HarmonyPatch(typeof(ZNetScene), "Awake")] internal static class ZNetSceneAwakePatch { private static void Postfix() { TamingOrbSystem.OnGameDataChanged(); FeedBarrelSystem.OnGameDataChanged(); FeedLikeGrandmaPlugin.QueueRuntimeRefresh(RuntimeRefreshFlags.All); } } [HarmonyPatch(typeof(ZNetView), "Awake")] internal static class ZNetViewAwakePatch { private static void Postfix(ZNetView __instance) { FeedSystem.RegisterSpawnedInstance(__instance); } } [HarmonyPatch(typeof(ZNetView), "OnDestroy")] internal static class ZNetViewOnDestroyPatch { private static void Prefix(ZNetView __instance) { FeedSystem.DeregisterSpawnedInstance(__instance); } } [HarmonyPatch(typeof(Player), "Awake")] internal static class PlayerAwakePatch { private static void Postfix(Player __instance) { FeedLikeGrandmaPlugin.QueueRuntimeRefresh(RuntimeRefreshFlags.FeedBarrel); FeedSystem.RegisterPlayerRpc(__instance); } } [HarmonyPatch(typeof(Tameable), "Awake")] internal static class TameableAwakePatch { private static void Postfix(Tameable __instance) { FeedSystem.RegisterTargetRpc(__instance.m_nview); } } [HarmonyPatch(typeof(Growup), "Start")] internal static class GrowupStartPatch { private static void Postfix(Growup __instance) { FeedSystem.RegisterTargetRpc(__instance.m_nview); } } [HarmonyPatch(typeof(Container), "Awake")] internal static class ContainerAwakePatch { private static void Postfix(Container __instance) { FeedAutoFeedSystem.RegisterContainer(__instance); } } [HarmonyPatch(typeof(Container), "OnDestroyed")] internal static class ContainerOnDestroyedPatch { private static void Prefix(Container __instance) { FeedAutoFeedSystem.DeregisterContainer(__instance); } } [HarmonyPatch(typeof(Tameable), "UseItem")] internal static class TameableUseItemPatch { private static bool Prefix(Tameable __instance, Humanoid user, ItemData item, ref bool __result) { if (TamingOrbSystem.TryHandleTameableUseItem(__instance, user, item)) { __result = true; return false; } if (!FeedSystem.TryHandleTameableUseItem(__instance, user, item)) { return true; } __result = true; return false; } } [HarmonyPatch(typeof(MonsterAI), "FindClosestConsumableItem")] internal static class MonsterAIFindClosestConsumableItemPatch { private static void Postfix(MonsterAI __instance, float maxRange, ref ItemDrop __result) { if (__result == null && FeedAutoFeedSystem.TryFindClosestConsumableFromFeedBarrel(__instance, maxRange, out ItemDrop consumable) && consumable != null) { __result = consumable; } } } [HarmonyPatch(typeof(Tameable), "OnConsumedItem")] internal static class TameableOnConsumedItemPatch { private struct AutomaticFeedState { public bool Handled; public float TargetHealth; } [HarmonyPriority(800)] private static bool Prefix(Tameable __instance, ItemDrop item, ref AutomaticFeedState __state) { if (!FeedSystem.TryHandleAutomaticConsumedItem(__instance, item, out var targetHealth)) { return true; } __state = new AutomaticFeedState { Handled = true, TargetHealth = targetHealth }; return false; } [HarmonyPriority(0)] [HarmonyAfter(new string[] { "Yggdrah.BetterRiding" })] private static void Postfix(Tameable __instance, AutomaticFeedState __state) { if (__state.Handled && FeedLikeGrandmaPlugin.BetterRidingFeedHealPriority.Value == FeedLikeGrandmaPlugin.Toggle.On && __instance != null) { Character component = ((Component)__instance).GetComponent(); if (component != null && component.m_nview != null && component.m_nview.IsValid() && component.m_nview.IsOwner() && !component.IsDead()) { component.SetHealth(Mathf.Clamp(__state.TargetHealth, 0f, component.GetMaxHealth())); } } } } [HarmonyPatch(typeof(MonsterAI), "CanConsume")] internal static class MonsterAICanConsumePatch { private static bool Prefix(MonsterAI __instance, ItemData item, ref bool __result) { if (!FeedSystem.TryCanAutoConsume(__instance, item, out var canConsume)) { return true; } __result = canConsume; return false; } } [HarmonyPatch(typeof(Humanoid), "UseItem")] internal static class HumanoidUseItemPatch { private static bool Prefix(Humanoid __instance, Inventory inventory, ItemData item, bool fromInventoryGui) { if (!fromInventoryGui) { Player val = (Player)(object)((__instance is Player) ? __instance : null); if (val != null && TamingOrbSystem.TryHandlePlayerUseItem(val, item)) { return false; } } if (fromInventoryGui) { return true; } return !FeedSystem.TryHandleGrowupUseItem(__instance, __instance.GetHoverObject(), item); } } [HarmonyPatch(typeof(Player), "UseHotbarItem")] internal static class PlayerUseHotbarItemPatch { private static bool Prefix(Player __instance, int index) { ItemData itemAt = ((Humanoid)__instance).GetInventory().GetItemAt(index - 1, 0); return !TamingOrbSystem.TryHandlePlayerUseItem(__instance, itemAt); } } [HarmonyPatch(typeof(ItemData), "GetTooltip", new Type[] { typeof(ItemData), typeof(int), typeof(bool), typeof(float), typeof(int) })] internal static class ItemDataGetTooltipPatch { private static void Postfix(ItemData item, bool crafting, ref string __result) { TamingOrbSystem.AppendTooltip(item, crafting, ref __result); } } [HarmonyPatch(typeof(ItemData), "GetStatusEffectTooltip")] internal static class ItemDataGetStatusEffectTooltipPatch { private static void Postfix(ItemData __instance, ref string __result) { if (MeadTamerTooltipSystem.TryBuildItemStatusEffectTooltip(__instance, out string tooltip)) { __result = tooltip; } } } [HarmonyPatch] internal static class StatusEffectGetTooltipStringPatch { private static IEnumerable TargetMethods() { MethodInfo methodInfo = AccessTools.Method(typeof(StatusEffect), "GetTooltipString", (Type[])null, (Type[])null); if (methodInfo != null) { yield return methodInfo; } MethodInfo methodInfo2 = AccessTools.Method(typeof(SE_Stats), "GetTooltipString", (Type[])null, (Type[])null); if (methodInfo2 != null) { yield return methodInfo2; } } private static void Postfix(StatusEffect __instance, ref string __result) { if (MeadTamerTooltipSystem.IsTamerPotion(__instance)) { __result = MeadTamerTooltipSystem.BuildTamerTooltip(); } } } internal static class MeadTamerTooltipSystem { private const string TamerPotionStatusEffectName = "Potion_tamer"; internal static bool TryBuildItemStatusEffectTooltip(ItemData item, out string tooltip) { tooltip = string.Empty; StatusEffect val = item?.m_shared?.m_consumeStatusEffect; if (!IsTamerPotion(val)) { return false; } string text = ((Localization.instance != null) ? Localization.instance.Localize(val.m_name) : val.m_name); tooltip = "" + text + "\n" + BuildTamerTooltip(); return true; } internal static bool IsTamerPotion(StatusEffect? statusEffect) { if (statusEffect == null) { return false; } return NormalizeUnityObjectName(((Object)statusEffect).name).Equals("Potion_tamer", StringComparison.OrdinalIgnoreCase); } internal static string BuildTamerTooltip() { return FeedLocalization.Text("$flg_mead_tamer_tooltip_header") + "\n" + FeedLocalization.Format("$flg_mead_tamer_tooltip_passive_tame", FormatMultiplier(FeedLikeGrandmaPlugin.GetTamingBoostMultiplier())) + "\n" + FeedLocalization.Format("$flg_mead_tamer_tooltip_passive_growth", FormatMultiplier(FeedLikeGrandmaPlugin.GetGrowthBoostMultiplier())) + "\n" + FeedLocalization.Format("$flg_mead_tamer_tooltip_feed", FormatMultiplier(FeedLikeGrandmaPlugin.GetFeedBoostMultiplier())) + "\n" + FeedLocalization.Text("$flg_mead_tamer_tooltip_feed_note"); } private static string FormatMultiplier(float value) { return "x" + Math.Max(1f, value).ToString("0.##", CultureInfo.InvariantCulture); } private static string NormalizeUnityObjectName(string name) { if (string.IsNullOrWhiteSpace(name)) { return string.Empty; } if (!name.EndsWith("(Clone)", StringComparison.Ordinal)) { return name.Trim(); } return name.Substring(0, name.Length - "(Clone)".Length).Trim(); } } [HarmonyPatch(typeof(ItemData), "GetIcon")] internal static class ItemDataGetIconPatch { private static void Prefix(ItemData __instance) { TamingOrbSystem.NormalizeVariant(__instance); } } [HarmonyPatch(typeof(ItemDrop), "GetHoverText")] internal static class ItemDropHoverTextPatch { private static void Postfix(ItemDrop __instance, ref string __result) { __result = TamingOrbSystem.AugmentDroppedItemHoverText(__instance, __result); } } [HarmonyPatch(typeof(Character), "GetHoverText")] internal static class CharacterHoverTextPatch { private static void Postfix(Character __instance, ref string __result) { __result = FeedSystem.AugmentHoverText(__instance, __result); } } [HarmonyPatch(typeof(EnemyHud), "TestShow")] internal static class EnemyHudTestShowPatch { private static bool Prefix(Character c, ref bool __result) { if (c != null && (Object)(object)c != (Object)null) { return true; } __result = false; return false; } } [HarmonyPatch(typeof(ZSyncTransform), "Awake")] internal static class ZSyncTransformAwakePatch { private static bool Prefix(ZSyncTransform __instance) { if (!TamingOrbSystem.ShouldSkipInventoryCloneWorldAwake((Component)(object)__instance)) { return true; } ((Behaviour)__instance).enabled = false; return false; } } [HarmonyPatch(typeof(Floating), "Awake")] internal static class FloatingAwakePatch { private static bool Prefix(Floating __instance) { if (!TamingOrbSystem.ShouldSkipInventoryCloneWorldAwake((Component)(object)__instance)) { return true; } ((Behaviour)__instance).enabled = false; return false; } } [HarmonyPatch(typeof(Tameable), "DecreaseRemainingTime")] internal static class TameableDecreaseRemainingTimePatch { private static void Prefix(Tameable __instance) { FeedSystem.ApplyConfiguredTamingMultiplier(__instance); } } [HarmonyPatch(typeof(Tameable), "IsHungry")] internal static class TameableIsHungryPatch { private static bool Prefix(Tameable __instance, ref bool __result) { if (!FeedSystem.TryGetHungryState(__instance, out var isHungry)) { return true; } __result = isHungry; return false; } } [HarmonyPatch(typeof(Growup), "GrowUpdate")] internal static class GrowupGrowUpdatePatch { private static bool Prefix(Growup __instance) { if (!FeedSystem.HasRuleFor(((Component)__instance).gameObject)) { return true; } return FeedSystem.HandleGrowUpdate(__instance); } } [HarmonyPatch(typeof(Procreation), "Procreate")] internal static class ProcreationProcreatePatch { private static bool Prefix(Procreation __instance) { return FeedSystem.ShouldRunProcreation(__instance); } } [HarmonyPatch(typeof(Hud), "UpdateCrosshair")] internal static class HudUpdateCrosshairPatch { private static void Postfix(Player player) { if (player != null) { FeedSystem.UpdateHoverIcons(player); } } } internal static class FeedLocalization { internal const string TamingOrbNameToken = "$flg_taming_orb"; internal const string TamingOrbDescriptionToken = "$flg_taming_orb_description"; internal const string FeedBarrelNameToken = "$flg_feed_barrel"; internal const string FeedBarrelDescriptionToken = "$flg_feed_barrel_description"; internal static void Load() { Localizer.Load(); } internal static string Text(string token) { if (Localization.instance != null) { return Localization.instance.Localize(token); } return token; } internal static string Format(string token, params object[] args) { string text = Text(token); try { return string.Format(CultureInfo.InvariantCulture, text, args); } catch (FormatException) { return text; } } internal static void Message(Player player, string token, params object[] args) { ((Character)player).Message((MessageType)2, Format(token, args), 0, (Sprite)null); } } internal static class FeedReferenceSections { private sealed class GroupedPrefab { public GameObject Prefab { get; init; } public string PrefabName { get; init; } = ""; public string OwnerName { get; init; } = "Unknown / Untracked"; } internal const string VanillaOwnerName = "Valheim"; internal const string UnknownOwnerName = "Unknown / Untracked"; internal static void AppendPrefabSections(StringBuilder builder, IEnumerable prefabs, Action appendEntry) { FeedAssetOwnerCatalog.RefreshMappings(); List> list = (from entry in prefabs.Where((GameObject prefab) => (Object)(object)prefab != (Object)null).Select(delegate(GameObject prefab) { string prefabName = (((Object)prefab).name ?? "").Trim(); string ownerName = FeedPrefabOwnerResolver.GetOwnerName(prefabName); return new GroupedPrefab { Prefab = prefab, PrefabName = prefabName, OwnerName = (string.IsNullOrWhiteSpace(ownerName) ? "Unknown / Untracked" : ownerName.Trim()) }; }) orderby GetOwnerSortBucket(entry.OwnerName) select entry).ThenBy((GroupedPrefab entry) => entry.OwnerName, StringComparer.OrdinalIgnoreCase).ThenBy((GroupedPrefab entry) => entry.PrefabName, StringComparer.OrdinalIgnoreCase).GroupBy((GroupedPrefab entry) => entry.OwnerName, StringComparer.OrdinalIgnoreCase) .ToList(); bool flag = false; foreach (IGrouping item in list) { if (flag) { builder.AppendLine(); } AppendSectionHeaderComment(builder, item.Key); bool flag2 = false; foreach (GroupedPrefab item2 in item) { if (flag2) { builder.AppendLine(); } appendEntry(builder, item2.Prefab); flag2 = true; } flag = true; } } private static void AppendSectionHeaderComment(StringBuilder builder, string ownerName) { builder.Append("# ===== "); builder.Append(string.IsNullOrWhiteSpace(ownerName) ? "Unknown / Untracked" : ownerName.Trim()); builder.AppendLine(" ====="); } private static int GetOwnerSortBucket(string ownerName) { if (string.Equals(ownerName, "Valheim", StringComparison.OrdinalIgnoreCase)) { return 0; } if (!string.Equals(ownerName, "Unknown / Untracked", StringComparison.OrdinalIgnoreCase)) { return 1; } return 2; } } internal static class FeedPrefabOwnerResolver { internal static string GetOwnerName(string? prefabName) { string text = NormalizeName(prefabName); if (text.Length == 0) { return "Unknown / Untracked"; } foreach (string item in EnumerateLookupCandidates(text)) { if (FeedVanillaPrefabCatalog.IsVanilla(item)) { return "Valheim"; } if (FeedExternalReferenceSupport.TryGetOwnerName(item, out string ownerName)) { return ownerName; } } return FeedAssetOwnerCatalog.GetOwnerName(text); } private static IEnumerable EnumerateLookupCandidates(string normalizedName) { HashSet seen = new HashSet(StringComparer.OrdinalIgnoreCase); AddIfNew(normalizedName); int num = normalizedName.IndexOf(':'); if (num > 0) { AddIfNew(normalizedName.Substring(0, num)); } foreach (string item in seen) { yield return item; } void AddIfNew(string candidate) { string text = NormalizeName(candidate); if (text.Length > 0) { seen.Add(text); } } } private static string NormalizeName(string? name) { return (name ?? "").Replace("(Clone)", "").Trim(); } } internal static class FeedVanillaPrefabCatalog { private enum CatalogState { Uninitialized, Loaded, Unavailable } private static readonly object Sync = new object(); private static readonly HashSet PrefabNames = new HashSet(StringComparer.OrdinalIgnoreCase); private static CatalogState State; internal static bool IsVanilla(string prefabName) { EnsureLoaded(); if (State == CatalogState.Loaded && !string.IsNullOrWhiteSpace(prefabName)) { return PrefabNames.Contains(prefabName); } return false; } private static void EnsureLoaded() { if (State != CatalogState.Uninitialized) { return; } lock (Sync) { if (State != CatalogState.Uninitialized) { return; } string text = Path.Combine(Application.dataPath, "StreamingAssets", "SoftRef", "manifest_extended"); if (!File.Exists(text)) { State = CatalogState.Unavailable; FeedLikeGrandmaPlugin.Log.LogWarning((object)("Vanilla prefab manifest was not found at '" + text + "'. Reference owner sections may place vanilla prefabs under 'Unknown / Untracked'.")); return; } foreach (string item in File.ReadLines(text)) { int num = item.IndexOf("path in bundle:", StringComparison.OrdinalIgnoreCase); if (num < 0) { continue; } string text2 = item.Substring(num + "path in bundle:".Length).Trim(); if (text2.EndsWith(".prefab", StringComparison.OrdinalIgnoreCase)) { string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(text2); if (!string.IsNullOrWhiteSpace(fileNameWithoutExtension)) { PrefabNames.Add(fileNameWithoutExtension); } } } State = CatalogState.Loaded; FeedLikeGrandmaPlugin.Log.LogDebug((object)$"Loaded {PrefabNames.Count} vanilla prefab names for reference owner sections."); } } } internal static class FeedAssetOwnerCatalog { private sealed class PluginResourceSnapshot { public string OwnerName { get; init; } = ""; public string PluginName { get; init; } = ""; public string PluginGuid { get; init; } = ""; public string AssemblyName { get; init; } = ""; public string[] ResourceNames { get; init; } = Array.Empty(); } private static readonly object Sync = new object(); private static readonly Dictionary AssetOwners = new Dictionary(StringComparer.OrdinalIgnoreCase); private static string LoadedSignature = ""; internal static string GetOwnerName(string assetName) { if (LoadedSignature.Length == 0) { RefreshMappings(); } foreach (string item in EnumerateLookupCandidates(assetName)) { if (AssetOwners.TryGetValue(item, out string value) && !string.IsNullOrWhiteSpace(value)) { return value; } } return "Unknown / Untracked"; } internal static void RefreshMappings() { string text = BuildSignature(); if (string.Equals(text, LoadedSignature, StringComparison.Ordinal)) { return; } lock (Sync) { if (string.Equals(text, LoadedSignature, StringComparison.Ordinal)) { return; } AssetOwners.Clear(); List pluginResources = GetPluginResources(); foreach (AssetBundle allLoadedAssetBundle in AssetBundle.GetAllLoadedAssetBundles()) { string text2 = ((Object)allLoadedAssetBundle).name ?? ""; if (text2.Length == 0) { continue; } string value = ResolveOwnerName(text2, pluginResources); if (string.IsNullOrWhiteSpace(value)) { continue; } string[] allAssetNames = allLoadedAssetBundle.GetAllAssetNames(); foreach (string text3 in allAssetNames) { if (text3.EndsWith(".prefab", StringComparison.OrdinalIgnoreCase)) { string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(text3); if (!string.IsNullOrWhiteSpace(fileNameWithoutExtension)) { AssetOwners[fileNameWithoutExtension] = value; } } } } LoadedSignature = text; FeedLikeGrandmaPlugin.Log.LogDebug((object)$"Tracked {AssetOwners.Count} prefab owner mapping(s) for reference sections."); } } private static IEnumerable EnumerateLookupCandidates(string assetName) { string normalizedName = (assetName ?? "").Replace("(Clone)", "").Trim(); if (normalizedName.Length != 0) { yield return normalizedName; int num = normalizedName.IndexOf(':'); if (num > 0) { yield return normalizedName.Substring(0, num); } } } private static List GetPluginResources() { return (from plugin in Chainloader.PluginInfos.Values.Select(delegate(PluginInfo pluginInfo) { string text = (pluginInfo.Metadata.Name ?? "").Trim(); string text2 = (pluginInfo.Metadata.GUID ?? "").Trim(); string assemblyName = ""; string[] resourceNames = Array.Empty(); try { assemblyName = ((object)pluginInfo.Instance)?.GetType().Assembly.GetName().Name ?? ""; resourceNames = ((object)pluginInfo.Instance)?.GetType().Assembly.GetManifestResourceNames() ?? Array.Empty(); } catch { } return new PluginResourceSnapshot { OwnerName = ((text.Length > 0) ? text : text2), PluginName = text, PluginGuid = text2, AssemblyName = assemblyName, ResourceNames = resourceNames }; }) where plugin.OwnerName.Length > 0 select plugin).ToList(); } private static string ResolveOwnerName(string bundleName, List plugins) { PluginResourceSnapshot pluginResourceSnapshot = plugins.FirstOrDefault((PluginResourceSnapshot plugin) => plugin.ResourceNames.Any((string resourceName) => resourceName.EndsWith(bundleName, StringComparison.OrdinalIgnoreCase))); if (pluginResourceSnapshot != null) { return pluginResourceSnapshot.OwnerName; } string normalizedBundleName = NormalizeToken(Path.GetFileNameWithoutExtension(bundleName)); if (normalizedBundleName.Length == 0) { return ""; } return plugins.FirstOrDefault(delegate(PluginResourceSnapshot plugin) { string pluginToken = NormalizeToken(plugin.PluginName); string pluginToken2 = NormalizeToken(plugin.PluginGuid); string pluginToken3 = NormalizeToken(plugin.AssemblyName); return IsTokenMatch(normalizedBundleName, pluginToken) || IsTokenMatch(normalizedBundleName, pluginToken2) || IsTokenMatch(normalizedBundleName, pluginToken3); })?.OwnerName ?? ""; } private static bool IsTokenMatch(string bundleName, string pluginToken) { if (pluginToken.Length > 0) { if (bundleName.IndexOf(pluginToken, StringComparison.OrdinalIgnoreCase) < 0) { return pluginToken.IndexOf(bundleName, StringComparison.OrdinalIgnoreCase) >= 0; } return true; } return false; } private static string NormalizeToken(string value) { if (string.IsNullOrWhiteSpace(value)) { return ""; } StringBuilder stringBuilder = new StringBuilder(); foreach (char c in value) { if (char.IsLetterOrDigit(c)) { stringBuilder.Append(char.ToLowerInvariant(c)); } } return stringBuilder.ToString(); } private static string BuildSignature() { IEnumerable values = (from bundle in AssetBundle.GetAllLoadedAssetBundles() select ((Object)bundle).name ?? "" into name where name.Length > 0 select name).OrderBy((string name) => name, StringComparer.OrdinalIgnoreCase); IEnumerable values2 = Chainloader.PluginInfos.Values.Select(delegate(PluginInfo pluginInfo) { string text = pluginInfo.Metadata.Name ?? ""; string text2 = pluginInfo.Metadata.GUID ?? ""; string text3 = ""; try { text3 = ((object)pluginInfo.Instance)?.GetType().Assembly.GetName().Name ?? ""; } catch { } return text2 + ":" + text + ":" + text3; }).OrderBy((string token) => token, StringComparer.OrdinalIgnoreCase); return string.Join("|", values) + "||" + string.Join("|", values2); } } internal static class FeedReferenceWriter { private sealed class ReferencePrefabSnapshot { public bool HasMonsterAI { get; init; } public bool HasTameable { get; init; } public float FedDuration { get; init; } public float TamingTime { get; init; } public bool StartsTamed { get; init; } public bool Commandable { get; init; } public bool HasProcreation { get; init; } public int MaxCreatures { get; init; } public float TotalCheckRange { get; init; } public bool HasGrowup { get; init; } public float GrowTime { get; init; } public bool HasEggGrow { get; init; } public float EggGrowTime { get; init; } public List ConsumeItemNames { get; init; } = new List(); private bool HasTameRelatedComponent { get { if (!HasTameable && !HasProcreation && !HasGrowup) { return HasEggGrow; } return true; } } public bool HasData => HasTameRelatedComponent; } private static readonly Dictionary ReferenceSnapshots = new Dictionary(StringComparer.OrdinalIgnoreCase); internal static string BuildDefaultOverrideYaml() { StringBuilder stringBuilder = new StringBuilder(); AppendTemplateComment(stringBuilder, "FeedLikeGrandma feed configuration."); AppendTemplateComment(stringBuilder, "Copy prefab blocks from feed.reference.yml. Omitted prefab fields keep the current game value."); AppendTemplateBlankLine(stringBuilder); AppendTemplateComment(stringBuilder, "Schema:"); AppendTemplateLine(stringBuilder, 0, "Boar: # creature prefab id."); AppendTemplateLine(stringBuilder, 1, "feed: [1, 1, 2, 0.5, 60] # overrides defaults.feed for this prefab."); AppendTemplateLine(stringBuilder, 1, "consumeItems: # everyday foods; ItemName or ItemName, localFeedNumber."); AppendTemplateLine(stringBuilder, 2, "- Raspberry"); AppendTemplateLine(stringBuilder, 2, "- Blueberries, 5"); AppendTemplateLine(stringBuilder, 1, "levelUpItems: # level-up-only foods keyed by current Character level."); AppendTemplateLine(stringBuilder, 2, "4: # Character level 4 -> 5. Stars: 3 -> 4."); AppendTemplateLine(stringBuilder, 3, "- Fish1@5, 500 # requires Fish1 quality 5 and adds 500 level-up progress."); AppendTemplateLine(stringBuilder, 1, "tame: [30, 1800, false, true] # fedDuration, tamingTime, startsTamed, commandable."); AppendTemplateLine(stringBuilder, 1, "procreation: [5, 10] # maxCreatures, totalCheckRange."); AppendTemplateLine(stringBuilder, 1, "grow: [3000] # growTime."); AppendTemplateLine(stringBuilder, 1, "eggGrow: [1800] # EggGrow growTime for egg item prefabs."); AppendTemplateLine(stringBuilder, 1, "mountStamina: [150, 80, 0.75] # BetterRiding attackStaminaCost, jumpStaminaCost, attackCooldown."); AppendTemplateLine(stringBuilder, 1, "restrictedBiome: [Ashlands] # BetterRiding dragon flight block list; use [] to opt out."); AppendTemplateBlankLine(stringBuilder); stringBuilder.AppendLine("defaults: # Global defaults used when a prefab omits feed."); AppendLine(stringBuilder, 1, "feed: [20, 15, 30, 25, 120] # levelupconstant, tamingconstant, growthconstant, healconstant, fedconstant."); AppendLine(stringBuilder, 1, "# effect = foodNumber * constant. levelup/taming/growth/fed are progress or seconds; heal is max-health percent."); AppendLine(stringBuilder, 1, "# levelUpItems override level-up food for that level; otherwise consumeItems can also add level progress."); AppendLine(stringBuilder, 1, "# level-up-only food still applies healconstant and fedconstant."); stringBuilder.AppendLine(); stringBuilder.AppendLine("foodNumbers: # Global numbers used by bare consumeItems; omitted values default to 1."); string[] array = new string[42] { "Mushroom", "Raspberry", "Honey, 2", "NeckTail, 1.5", "NeckTailGrilled, 2", "RawMeat, 1.5", "CookedMeat, 2", "DeerMeat, 2", "CookedDeerMeat, 2.5", "Blueberries, 2", "Carrot, 2", "BjornMeat, 3", "CookedBjornMeat, 4", "Turnip, 2", "Sausages, 4", "FishRaw, 3", "FishCooked, 4", "SerpentMeat, 4", "SerpentMeatCooked, 5", "Onion, 3", "WolfMeat, 2", "CookedWolfMeat, 3", "ChickenMeat, 4", "CookedChickenMeat, 5", "MushroomJotunPuffs, 2", "Barley, 2", "Cloudberry, 2", "Flax, 2", "LoxMeat, 3", "CookedLoxMeat, 4", "HareMeat, 4", "CookedHareMeat, 6", "MushroomMagecap, 2", "AsksvinMeat, 3", "CookedAsksvinMeat, 5", "VoltureMeat, 3", "CookedVoltureMeat, 5", "BoneMawSerpentMeat, 5", "CookedBoneMawSerpentMeat, 8", "Fiddleheadfern, 3", "MushroomSmokePuff, 3", "Vineberry, 2" }; foreach (string text in array) { AppendLine(stringBuilder, 1, "- " + text); } stringBuilder.AppendLine(); stringBuilder.AppendLine("Asksvin:"); AppendLine(stringBuilder, 1, "levelUpItems: # level-up-only foods keyed by current Character level."); AppendLine(stringBuilder, 2, "2: # Character level 2 -> 3. Stars: 1 -> 2."); AppendLine(stringBuilder, 3, "- Fish11, 300"); stringBuilder.AppendLine(); stringBuilder.AppendLine("#DModer_Ygg4_Elder:"); AppendLine(stringBuilder, 0, "# levelUpItems:"); AppendLine(stringBuilder, 0, "# 1:"); AppendLine(stringBuilder, 0, "# - Fish4_cave@2, 300"); return stringBuilder.ToString(); } internal static string BuildReferenceYaml(bool refreshExternalSnapshot = true) { if (refreshExternalSnapshot) { FeedExternalReferenceSupport.RefreshSnapshot(); } List externalPrefabs = FeedExternalReferenceSupport.GetExternalPrefabs().ToList(); EnsureReferenceSnapshotAvailable(externalPrefabs); List feedPrefabs = GetFeedPrefabs(externalPrefabs); if (feedPrefabs.Count == 0) { return "[]\n"; } StringBuilder stringBuilder = new StringBuilder(); AppendGeneratedHeader(stringBuilder, "feed reference"); FeedReferenceSections.AppendPrefabSections(stringBuilder, feedPrefabs, AppendPrefabEntry); return stringBuilder.ToString(); } internal static void CaptureMissingReferenceSnapshots(IEnumerable prefabs) { foreach (GameObject item in from @group in prefabs.Where((GameObject prefab) => (Object)(object)prefab != (Object)null && !string.IsNullOrWhiteSpace(((Object)prefab).name)).GroupBy((GameObject prefab) => ((Object)prefab).name, StringComparer.OrdinalIgnoreCase) select @group.First() into prefab where !FeedSystem.ShouldIgnorePrefab(prefab) select prefab) { if (!ReferenceSnapshots.ContainsKey(((Object)item).name)) { ReferencePrefabSnapshot referencePrefabSnapshot = CaptureReferenceSnapshot(item); if (referencePrefabSnapshot.HasData) { ReferenceSnapshots[((Object)item).name] = referencePrefabSnapshot; } } } } internal static void RestoreReferenceSnapshots(IEnumerable prefabs) { if (ReferenceSnapshots.Count == 0) { return; } foreach (GameObject item in from @group in prefabs.Where((GameObject prefab) => (Object)(object)prefab != (Object)null && !string.IsNullOrWhiteSpace(((Object)prefab).name)).GroupBy((GameObject prefab) => ((Object)prefab).name, StringComparer.OrdinalIgnoreCase) select @group.First()) { if (ReferenceSnapshots.TryGetValue(((Object)item).name, out ReferencePrefabSnapshot value) && value != null) { RestoreReferenceSnapshot(item, value); } } } private static void EnsureReferenceSnapshotAvailable(List? externalPrefabs = null) { bool num = ZNetScene.instance?.m_prefabs != null; if (externalPrefabs == null) { externalPrefabs = FeedExternalReferenceSupport.GetExternalPrefabs().ToList(); } if (num || externalPrefabs.Count != 0) { CaptureMissingReferenceSnapshots(EnumerateReferencePrefabs(externalPrefabs)); } } internal static void Shutdown() { ReferenceSnapshots.Clear(); } private static List GetFeedPrefabs(List? externalPrefabs = null) { if (externalPrefabs == null) { externalPrefabs = FeedExternalReferenceSupport.GetExternalPrefabs().ToList(); } if (((Object)(object)ZNetScene.instance == (Object)null || ZNetScene.instance.m_prefabs == null) && externalPrefabs.Count == 0) { return new List(); } ReferencePrefabSnapshot value; return (from @group in (from prefab in EnumerateReferencePrefabs(externalPrefabs) where (Object)(object)prefab != (Object)null && !string.IsNullOrWhiteSpace(((Object)prefab).name) select prefab).GroupBy((GameObject prefab) => ((Object)prefab).name, StringComparer.OrdinalIgnoreCase) select @group.First() into prefab where !FeedSystem.ShouldIgnorePrefab(prefab) where ReferenceSnapshots.TryGetValue(((Object)prefab).name, out value) && ShouldIncludeInReference(value) select prefab).OrderBy((GameObject prefab) => ((Object)prefab).name, StringComparer.OrdinalIgnoreCase).ToList(); } private static void AppendPrefabEntry(StringBuilder builder, GameObject prefab) { if (!ReferenceSnapshots.TryGetValue(((Object)prefab).name, out ReferencePrefabSnapshot value)) { return; } bool flag = ShouldEmitProcreation(value); bool flag2 = ShouldEmitGrowup(value); bool flag3 = value.HasTameable || flag || flag2 || value.ConsumeItemNames.Count > 0; bool hasEggGrow = value.HasEggGrow; if (!flag3 && !hasEggGrow) { return; } AppendLine(builder, 0, FormatKey(((Object)prefab).name) + ":"); if (flag3) { AppendConsumeItems(builder, value.ConsumeItemNames); if (value.HasTameable) { AppendTameable(builder, value); } if (flag) { AppendProcreation(builder, value); } if (flag2) { AppendGrowup(builder, value); } } if (hasEggGrow) { AppendEggGrow(builder, value); } } private static bool ShouldIncludeInReference(ReferencePrefabSnapshot snapshot) { if (!snapshot.HasTameable && !ShouldEmitProcreation(snapshot) && !ShouldEmitGrowup(snapshot) && !snapshot.HasEggGrow) { return snapshot.ConsumeItemNames.Count > 0; } return true; } private static bool ShouldEmitProcreation(ReferencePrefabSnapshot snapshot) { if (snapshot.HasProcreation) { if (snapshot.MaxCreatures == 0) { return Math.Abs(snapshot.TotalCheckRange) > 0.0001f; } return true; } return false; } private static bool ShouldEmitGrowup(ReferencePrefabSnapshot snapshot) { if (snapshot.HasGrowup) { return Math.Abs(snapshot.GrowTime) > 0.0001f; } return false; } private static ReferencePrefabSnapshot CaptureReferenceSnapshot(GameObject prefab) { Tameable component = prefab.GetComponent(); Procreation component2 = prefab.GetComponent(); Growup component3 = prefab.GetComponent(); EggGrow component4 = prefab.GetComponent(); MonsterAI component5 = prefab.GetComponent(); List consumeItemNames = (from name in FeedSystem.ResolveConsumeData(prefab).ConsumeItems.Where((ItemDrop item) => (Object)(object)item != (Object)null).Select(FeedExternalReferenceSupport.ExtractItemDropPrefabName).Concat(FeedExternalReferenceSupport.GetConfiguredConsumeItemNames(prefab)) where !string.IsNullOrWhiteSpace(name) select name).Distinct(StringComparer.OrdinalIgnoreCase).OrderBy((string name) => name, StringComparer.OrdinalIgnoreCase).ToList(); return new ReferencePrefabSnapshot { HasMonsterAI = ((Object)(object)component5 != (Object)null), HasTameable = ((Object)(object)component != (Object)null), FedDuration = (component?.m_fedDuration ?? 0f), TamingTime = (component?.m_tamingTime ?? 0f), StartsTamed = (component?.m_startsTamed ?? false), Commandable = (component?.m_commandable ?? false), HasProcreation = ((Object)(object)component2 != (Object)null), MaxCreatures = (component2?.m_maxCreatures ?? 0), TotalCheckRange = (component2?.m_totalCheckRange ?? 0f), HasGrowup = ((Object)(object)component3 != (Object)null), GrowTime = (component3?.m_growTime ?? 0f), HasEggGrow = ((Object)(object)component4 != (Object)null), EggGrowTime = (component4?.m_growTime ?? 0f), ConsumeItemNames = consumeItemNames }; } private static IEnumerable EnumerateReferencePrefabs(IEnumerable? externalPrefabs = null) { if (ZNetScene.instance?.m_prefabs != null) { foreach (GameObject prefab in ZNetScene.instance.m_prefabs) { yield return prefab; } } foreach (GameObject item in externalPrefabs ?? FeedExternalReferenceSupport.GetExternalPrefabs()) { yield return item; } } private static void RestoreReferenceSnapshot(GameObject prefab, ReferencePrefabSnapshot snapshot) { if (snapshot.HasMonsterAI) { MonsterAI component = prefab.GetComponent(); if (component != null) { component.m_consumeItems = ResolveItemDrops(snapshot.ConsumeItemNames); } } if (snapshot.HasTameable) { Tameable component2 = prefab.GetComponent(); if (component2 != null) { component2.m_fedDuration = snapshot.FedDuration; component2.m_tamingTime = snapshot.TamingTime; component2.m_startsTamed = snapshot.StartsTamed; component2.m_commandable = snapshot.Commandable; } } if (snapshot.HasProcreation) { Procreation component3 = prefab.GetComponent(); if (component3 != null) { component3.m_maxCreatures = snapshot.MaxCreatures; component3.m_totalCheckRange = snapshot.TotalCheckRange; } } if (snapshot.HasGrowup) { Growup component4 = prefab.GetComponent(); if (component4 != null) { component4.m_growTime = snapshot.GrowTime; } } if (snapshot.HasEggGrow) { EggGrow component5 = prefab.GetComponent(); if (component5 != null) { component5.m_growTime = snapshot.EggGrowTime; } } } private static List ResolveItemDrops(IEnumerable itemNames) { List list = new List(); if (ObjectDB.instance == null) { return list; } foreach (string itemName in itemNames) { GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(itemName); ItemDrop val = (((Object)(object)itemPrefab == (Object)null) ? null : itemPrefab.GetComponent()); if (val != null) { list.Add(val); } } return list; } private static void AppendConsumeItems(StringBuilder builder, List itemNames) { if (itemNames.Count == 0) { AppendLine(builder, 1, "consumeItems: []"); return; } AppendLine(builder, 1, "consumeItems:"); foreach (string itemName in itemNames) { AppendLine(builder, 2, "- " + FormatYamlString(itemName)); } } private static void AppendTameable(StringBuilder builder, ReferencePrefabSnapshot snapshot) { AppendLine(builder, 1, "tame: " + FormatInlineRawList(FormatFloat(snapshot.FedDuration), FormatFloat(snapshot.TamingTime), FormatBool(snapshot.StartsTamed), FormatBool(snapshot.Commandable))); } private static void AppendProcreation(StringBuilder builder, ReferencePrefabSnapshot snapshot) { AppendLine(builder, 1, "procreation: " + FormatInlineRawList(snapshot.MaxCreatures.ToString(CultureInfo.InvariantCulture), FormatFloat(snapshot.TotalCheckRange))); } private static void AppendGrowup(StringBuilder builder, ReferencePrefabSnapshot snapshot) { AppendLine(builder, 1, "grow: [" + FormatFloat(snapshot.GrowTime) + "]"); } private static void AppendEggGrow(StringBuilder builder, ReferencePrefabSnapshot snapshot) { AppendLine(builder, 1, "eggGrow: [" + FormatFloat(snapshot.EggGrowTime) + "]"); } private static void AppendGeneratedHeader(StringBuilder builder, string artifactKind) { builder.AppendLine("# Generated by FeedLikeGrandma (" + artifactKind + ")."); builder.AppendLine("# Owner sections are best-effort guesses from the Valheim manifest and loaded asset bundles."); builder.AppendLine("# Includes prefabs with Tameable, Procreation, Growup, or EggGrow components."); builder.AppendLine("# Copy blocks into feed.yml and remove fields you do not want to override."); builder.AppendLine(); } private static void AppendTemplateComment(StringBuilder builder, string text) { builder.Append("# "); builder.AppendLine(text); } private static void AppendTemplateBlankLine(StringBuilder builder) { builder.AppendLine("#"); } private static void AppendTemplateLine(StringBuilder builder, int indent, string text) { builder.Append("# "); builder.Append(' ', indent * 2); builder.AppendLine(text); } private static void AppendLine(StringBuilder builder, int indent, string text) { builder.Append(' ', indent * 2); builder.AppendLine(text); } private static string FormatInlineRawList(params string[] values) { return "[" + string.Join(", ", values) + "]"; } private static string FormatFloat(float value) { return value.ToString("0.###", CultureInfo.InvariantCulture); } private static string FormatBool(bool value) { if (!value) { return "false"; } return "true"; } private static string FormatKey(string value) { return FormatYamlString(value); } private static string FormatYamlString(string? value) { string text = value ?? ""; if (text.Length > 0 && text.All(delegate(char character) { bool flag = char.IsLetterOrDigit(character); if (!flag) { bool flag2; switch (character) { case '$': case '-': case '.': case '/': case '_': flag2 = true; break; default: flag2 = false; break; } flag = flag2; } return flag; })) { return text; } return "'" + text.Replace("'", "''") + "'"; } } internal static class FeedRidingSystem { internal readonly struct StaminaRegenState { internal float Regen { get; init; } internal float RegenHungry { get; init; } } private static int _encumberedPlayerId; private static bool _encumberedCacheValid; private static bool _encumberedCached; internal static void Shutdown() { _encumberedPlayerId = 0; _encumberedCacheValid = false; _encumberedCached = false; } internal static bool ShouldSkipMountCameraTilt(Player player) { if (FeedLikeGrandmaPlugin.StableMountCamera.Value.IsOn() && player != null) { return ((Character)player).IsRiding(); } return false; } internal static bool IsLocalRiderEncumbered() { Player localPlayer = Player.m_localPlayer; if (localPlayer != null) { return IsPlayerEncumberedCached(localPlayer); } return false; } internal static bool IsPlayerEncumberedCached(Player player) { if (player == null) { return false; } int instanceID = ((Object)player).GetInstanceID(); if (!_encumberedCacheValid || _encumberedPlayerId != instanceID) { RefreshPlayerEncumbered(player); } if (_encumberedPlayerId == instanceID) { return _encumberedCached; } return false; } internal static void RefreshPlayerEncumbered(Player player) { if (player != null) { _encumberedPlayerId = ((Object)player).GetInstanceID(); _encumberedCached = ((Humanoid)player).m_inventory.GetTotalWeight() > player.GetMaxCarryWeight(); _encumberedCacheValid = true; } } internal static bool ShouldTreatMountAsEncumbered(Character character) { if (FeedLikeGrandmaPlugin.EncumberedMountSpeedReduction.Value != FeedLikeGrandmaPlugin.Toggle.On || character == null) { return false; } Sadle val = character.m_baseAI?.m_tamable?.m_saddle; if (val != null && val.IsLocalUser()) { return IsLocalRiderEncumbered(); } return false; } internal static void ApplyStaminaCostMultiplier(Sadle saddle, ref float staminaCost) { if (saddle != null && saddle.m_nview != null && saddle.m_nview.IsValid() && saddle.m_nview.IsOwner()) { float num = (IsLocalRiderEncumbered() ? FeedLikeGrandmaPlugin.EncumberedMountStaminaDrainMultiplier.Value : FeedLikeGrandmaPlugin.MountStaminaDrainMultiplier.Value); staminaCost *= Mathf.Max(0f, num); } } internal static StaminaRegenState ApplyStaminaRegenMultiplier(Sadle saddle) { StaminaRegenState result = new StaminaRegenState { Regen = saddle.m_staminaRegen, RegenHungry = saddle.m_staminaRegenHungry }; float num = (IsLocalRiderEncumbered() ? FeedLikeGrandmaPlugin.EncumberedMountStaminaRegenMultiplier.Value : FeedLikeGrandmaPlugin.MountStaminaRegenMultiplier.Value); float num2 = Mathf.Clamp01(saddle.GetRiderSkill()); float num3 = Mathf.Max(1f, FeedLikeGrandmaPlugin.RidingSkillStaminaRegenMultiplierAt100.Value); float num4 = Mathf.Lerp(1f, num3, num2); num = Mathf.Max(0f, num) * num4; saddle.m_staminaRegen *= num; saddle.m_staminaRegenHungry *= num; return result; } internal static void RestoreStaminaRegen(Sadle saddle, StaminaRegenState state) { saddle.m_staminaRegen = state.Regen; saddle.m_staminaRegenHungry = state.RegenHungry; } internal static void DrainEncumberedWalkStamina(Sadle saddle, float dt, bool updatedRiding) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Invalid comparison between Unknown and I4 if (updatedRiding && FeedLikeGrandmaPlugin.EncumberedWalkStaminaDrain.Value == FeedLikeGrandmaPlugin.Toggle.On && saddle != null && (int)saddle.m_speed == 1 && IsLocalRiderEncumbered()) { float num = Mathf.Lerp(1f, 0.5f, saddle.GetRiderSkill()); saddle.UseStamina(saddle.m_runStaminaDrain * num * dt); } } } [HarmonyPatch(typeof(GameCamera), "ApplyCameraTilt")] internal static class GameCameraApplyCameraTiltPatch { private static bool Prefix(Player player) { return !FeedRidingSystem.ShouldSkipMountCameraTilt(player); } } [HarmonyPatch(typeof(Sadle), "UseStamina")] internal static class SaddleUseStaminaPatch { private static void Prefix(Sadle __instance, ref float v) { FeedRidingSystem.ApplyStaminaCostMultiplier(__instance, ref v); } } [HarmonyPatch(typeof(Sadle), "UpdateStamina")] internal static class SaddleUpdateStaminaPatch { private static void Prefix(Sadle __instance, ref FeedRidingSystem.StaminaRegenState __state) { __state = FeedRidingSystem.ApplyStaminaRegenMultiplier(__instance); } private static void Postfix(Sadle __instance, FeedRidingSystem.StaminaRegenState __state) { FeedRidingSystem.RestoreStaminaRegen(__instance, __state); } } [HarmonyPatch(typeof(Sadle), "UpdateRiding")] internal static class SaddleUpdateRidingPatch { private static void Postfix(Sadle __instance, float dt, bool __result) { FeedRidingSystem.DrainEncumberedWalkStamina(__instance, dt, __result); } } [HarmonyPatch(typeof(Character), "IsEncumbered")] internal static class CharacterIsEncumberedForMountPatch { private static void Postfix(Character __instance, ref bool __result) { if (FeedRidingSystem.ShouldTreatMountAsEncumbered(__instance)) { __result = true; } } } [HarmonyPatch(typeof(Player), "OnInventoryChanged")] internal static class PlayerOnInventoryChangedEncumberedCachePatch { private static void Postfix(Player __instance) { if (((Character)__instance).IsRiding()) { FeedRidingSystem.RefreshPlayerEncumbered(__instance); } } } [HarmonyPatch(typeof(Player), "StartDoodadControl")] internal static class PlayerStartDoodadControlEncumberedCachePatch { private static void Postfix(Player __instance) { FeedRidingSystem.RefreshPlayerEncumbered(__instance); } } internal sealed class FeedConsumeData { public bool InheritedConsumeItems { get; init; } public List ConsumeItems { get; init; } = new List(); } internal static class FeedSystem { private readonly struct HoverFoodRows { public IReadOnlyList LevelUpFoods { get; } public IReadOnlyList DailyFoods { get; } public int CurrentLevel { get; } public int TotalCount => LevelUpFoods.Count + DailyFoods.Count; public HoverFoodRows(IReadOnlyList levelUpFoods, IReadOnlyList dailyFoods, int currentLevel) { LevelUpFoods = levelUpFoods; DailyFoods = dailyFoods; CurrentLevel = currentLevel; } } private sealed class HoverFoodIconHud { private readonly List _levelUpIcons = new List(); private readonly List _dailyIcons = new List(); private readonly GameObject _root; private readonly RectTransform _rootTransform; private readonly GameObject? _levelUpRow; private readonly RectTransform? _levelUpRowTransform; private readonly HoverFoodRowLabel? _levelUpLabel; private readonly GameObject? _dailyRow; private readonly RectTransform? _dailyRowTransform; private IReadOnlyList? _shownLevelUpFoods; private IReadOnlyList? _shownDailyFoods; private int _shownCurrentLevel; private string _shownLanguage = string.Empty; private float _shownIconSize = -1f; private bool _contentShown; private float _nextAnchorUpdateTime; public HoverFoodIconHud() { //IL_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: Expected O, but got Unknown //IL_0103: Unknown result type (might be due to invalid IL or missing references) //IL_011d: Unknown result type (might be due to invalid IL or missing references) //IL_0137: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Expected O, but got Unknown if (Hud.instance == null) { _root = new GameObject("FeedLikeGrandmaHoverIcons"); _rootTransform = _root.AddComponent(); _levelUpRow = null; _levelUpRowTransform = null; _levelUpLabel = null; _dailyRow = null; _dailyRowTransform = null; _root.SetActive(false); return; } _root = new GameObject("FeedLikeGrandmaHoverIcons", new Type[3] { typeof(RectTransform), typeof(VerticalLayoutGroup), typeof(ContentSizeFitter) }); _rootTransform = _root.GetComponent(); ((Transform)_rootTransform).SetParent(Hud.instance.m_rootObject.transform, false); _rootTransform.anchorMin = new Vector2(0.5f, 0.5f); _rootTransform.anchorMax = new Vector2(0.5f, 0.5f); _rootTransform.pivot = new Vector2(0f, 0f); VerticalLayoutGroup component = _root.GetComponent(); ((LayoutGroup)component).childAlignment = (TextAnchor)0; ((HorizontalOrVerticalLayoutGroup)component).childForceExpandWidth = false; ((HorizontalOrVerticalLayoutGroup)component).childForceExpandHeight = false; ((HorizontalOrVerticalLayoutGroup)component).spacing = GetRowSpacing(); ContentSizeFitter component2 = _root.GetComponent(); component2.horizontalFit = (FitMode)2; component2.verticalFit = (FitMode)2; _levelUpRow = CreateRow("LevelUp", _rootTransform, out _levelUpRowTransform); _levelUpLabel = HoverFoodRowLabel.Create(_levelUpRowTransform); _dailyRow = CreateRow("Daily", _rootTransform, out _dailyRowTransform); _root.SetActive(false); } public void Show(HoverFoodRows rows) { //IL_019e: Unknown result type (might be due to invalid IL or missing references) if (Hud.instance != null) { float value = FeedLikeGrandmaPlugin.FoodIconSize.Value; string text = ((Localization.instance == null) ? string.Empty : Localization.instance.GetSelectedLanguage()); bool flag = !_contentShown || _shownLevelUpFoods != rows.LevelUpFoods || _shownDailyFoods != rows.DailyFoods || _shownCurrentLevel != rows.CurrentLevel || !string.Equals(_shownLanguage, text, StringComparison.Ordinal) || !Approximately(_shownIconSize, value); if (flag) { ((HorizontalOrVerticalLayoutGroup)_root.GetComponent()).spacing = GetRowSpacing(); string labelText = ((rows.CurrentLevel > 0) ? FeedLocalization.Format("$flg_food_icon_level_label", rows.CurrentLevel.ToString(CultureInfo.InvariantCulture), (rows.CurrentLevel + 1).ToString(CultureInfo.InvariantCulture)) : string.Empty); ShowRow(_levelUpRow, _levelUpRowTransform, _levelUpIcons, rows.LevelUpFoods, _levelUpLabel, labelText, value); ShowRow(_dailyRow, _dailyRowTransform, _dailyIcons, rows.DailyFoods, null, string.Empty, value); _shownLevelUpFoods = rows.LevelUpFoods; _shownDailyFoods = rows.DailyFoods; _shownCurrentLevel = rows.CurrentLevel; _shownLanguage = text; _shownIconSize = value; _contentShown = true; } if (!_root.activeSelf || flag || Time.unscaledTime >= _nextAnchorUpdateTime) { _rootTransform.anchoredPosition = GetHoverTextIconAnchor(); _nextAnchorUpdateTime = Time.unscaledTime + 0.1f; } SetActiveIfChanged(_root, rows.TotalCount > 0); } } public void Hide() { SetActiveIfChanged(_root, active: false); } public void Destroy() { if (_root != null) { Object.Destroy((Object)(object)_root); } } private static GameObject CreateRow(string name, RectTransform parent, out RectTransform rowTransform) { //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Expected O, but got Unknown GameObject val = new GameObject("FeedLikeGrandma" + name + "FoodRow", new Type[3] { typeof(RectTransform), typeof(HorizontalLayoutGroup), typeof(ContentSizeFitter) }); rowTransform = val.GetComponent(); ((Transform)rowTransform).SetParent((Transform)(object)parent, false); HorizontalLayoutGroup component = val.GetComponent(); ((LayoutGroup)component).childAlignment = (TextAnchor)3; ((HorizontalOrVerticalLayoutGroup)component).childForceExpandWidth = false; ((HorizontalOrVerticalLayoutGroup)component).childForceExpandHeight = false; ((HorizontalOrVerticalLayoutGroup)component).spacing = 6f; ContentSizeFitter component2 = val.GetComponent(); component2.horizontalFit = (FitMode)2; component2.verticalFit = (FitMode)2; val.SetActive(false); return val; } private static void ShowRow(GameObject? rowObject, RectTransform? rowTransform, List icons, IReadOnlyList foods, HoverFoodRowLabel? label, string labelText, float iconSize) { //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) if (rowObject == null || rowTransform == null) { return; } EnsureIconCount(rowTransform, icons, foods.Count); ((HorizontalOrVerticalLayoutGroup)rowObject.GetComponent()).spacing = 6f; for (int i = 0; i < icons.Count; i++) { bool flag = i < foods.Count; SetActiveIfChanged(icons[i].GameObject, flag); if (flag) { icons[i].RectTransform.sizeDelta = Vector2.one * iconSize; icons[i].SetFood(foods[i], iconSize); } } label?.SetText(labelText, iconSize); SetActiveIfChanged(rowObject, foods.Count > 0); } private static void EnsureIconCount(RectTransform parent, List icons, int count) { while (icons.Count < count) { icons.Add(HoverFoodIcon.Create(parent, icons.Count)); } } private static float GetRowSpacing() { return Mathf.Max(1f, 2.1f); } private static void SetActiveIfChanged(GameObject gameObject, bool active) { if (gameObject != null && gameObject.activeSelf != active) { gameObject.SetActive(active); } } private static Vector2 GetHoverTextIconAnchor() { //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Unknown result type (might be due to invalid IL or missing references) //IL_00e0: Unknown result type (might be due to invalid IL or missing references) //IL_00e5: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_00c0: Unknown result type (might be due to invalid IL or missing references) //IL_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_00ca: Unknown result type (might be due to invalid IL or missing references) //IL_00e7: Unknown result type (might be due to invalid IL or missing references) //IL_00e8: Unknown result type (might be due to invalid IL or missing references) //IL_00ed: Unknown result type (might be due to invalid IL or missing references) //IL_00ef: Unknown result type (might be due to invalid IL or missing references) //IL_00f6: Unknown result type (might be due to invalid IL or missing references) //IL_0103: Unknown result type (might be due to invalid IL or missing references) if (Hud.instance == null || Hud.instance.m_hoverName == null || Hud.instance.m_rootObject == null) { return new Vector2(0f, 6f); } Transform transform = Hud.instance.m_rootObject.transform; RectTransform val = (RectTransform)(object)((transform is RectTransform) ? transform : null); RectTransform rectTransform = ((TMP_Text)Hud.instance.m_hoverName).rectTransform; if (val == null || rectTransform == null) { return new Vector2(0f, 6f); } ((TMP_Text)Hud.instance.m_hoverName).ForceMeshUpdate(false, false); Bounds textBounds = ((TMP_Text)Hud.instance.m_hoverName).textBounds; Vector3 size = ((Bounds)(ref textBounds)).size; Vector3 val2; if (((Vector3)(ref size)).sqrMagnitude > 0.001f) { val2 = ((Transform)rectTransform).TransformPoint(new Vector3(((Bounds)(ref textBounds)).min.x, ((Bounds)(ref textBounds)).max.y, 0f)); } else { Vector3[] array = (Vector3[])(object)new Vector3[4]; rectTransform.GetWorldCorners(array); val2 = array[1]; } Vector3 val3 = ((Transform)val).InverseTransformPoint(val2); return new Vector2(val3.x, val3.y + 6f); } } private sealed class HoverFoodRowLabel { private readonly GameObject _gameObject; private readonly RectTransform _rectTransform; private readonly TextMeshProUGUI _text; private readonly LayoutElement _layoutElement; private string _lastText = string.Empty; private float _lastIconSize = -1f; private HoverFoodRowLabel(GameObject gameObject, RectTransform rectTransform, TextMeshProUGUI text, LayoutElement layoutElement) { _gameObject = gameObject; _rectTransform = rectTransform; _text = text; _layoutElement = layoutElement; } public static HoverFoodRowLabel Create(RectTransform parent) { //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Expected O, but got Unknown //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_00e5: Unknown result type (might be due to invalid IL or missing references) //IL_00f9: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject("FeedLikeGrandmaLevelUpLabel", new Type[3] { typeof(RectTransform), typeof(CanvasRenderer), typeof(LayoutElement) }); RectTransform component = val.GetComponent(); ((Transform)component).SetParent((Transform)(object)parent, false); TextMeshProUGUI val2 = val.AddComponent(); ((TMP_Text)val2).alignment = (TextAlignmentOptions)4097; ((Graphic)val2).color = new Color(1f, 0.62f, 0.16f, 1f); ((TMP_Text)val2).enableAutoSizing = true; ((TMP_Text)val2).fontSizeMin = 8f; ((TMP_Text)val2).fontSizeMax = 18f; ((TMP_Text)val2).fontStyle = (FontStyles)1; ((TMP_Text)val2).textWrappingMode = (TextWrappingModes)0; ((Graphic)val2).raycastTarget = false; ApplyHudFont((TMP_Text)(object)val2, (TMP_Text?)(object)(((Object)(object)Hud.instance != (Object)null) ? Hud.instance.m_hoverName : null)); Shadow obj = val.AddComponent(); obj.effectColor = new Color(0f, 0f, 0f, 0.9f); obj.effectDistance = new Vector2(1f, -1f); return new HoverFoodRowLabel(val, component, val2, val.GetComponent()); } public void SetText(string text, float iconSize) { //IL_0104: Unknown result type (might be due to invalid IL or missing references) bool flag = !string.IsNullOrWhiteSpace(text); if (_gameObject.activeSelf != flag) { _gameObject.SetActive(flag); } if (!flag) { _lastText = string.Empty; _lastIconSize = -1f; } else if (!string.Equals(_lastText, text, StringComparison.Ordinal) || !Approximately(_lastIconSize, iconSize)) { float num = Mathf.Clamp(iconSize * 0.34f, 8f, 18f); ((TMP_Text)_text).fontSizeMax = num; ((TMP_Text)_text).fontSize = num; ((TMP_Text)_text).text = text; ((TMP_Text)_text).ForceMeshUpdate(false, false); float num2 = Mathf.Ceil(((TMP_Text)_text).preferredWidth); float num3 = Mathf.Max(iconSize, Mathf.Ceil(((TMP_Text)_text).preferredHeight)); _layoutElement.minWidth = num2; _layoutElement.preferredWidth = num2; _layoutElement.minHeight = num3; _layoutElement.preferredHeight = num3; _rectTransform.sizeDelta = new Vector2(num2, num3); ((Transform)_rectTransform).SetAsLastSibling(); _lastText = text; _lastIconSize = iconSize; } } } private sealed class HoverFoodIcon { private readonly Image _image; private readonly LayoutElement _layoutElement; private readonly TextMeshProUGUI _qualityText; private readonly TextMeshProUGUI _valueText; private Sprite? _lastSprite; private int? _lastRequiredQuality; private float _lastValue = float.NaN; private float _lastIconSize = -1f; public GameObject GameObject { get; } public RectTransform RectTransform { get; } private HoverFoodIcon(GameObject gameObject, RectTransform rectTransform, Image image, LayoutElement layoutElement, TextMeshProUGUI qualityText, TextMeshProUGUI valueText) { GameObject = gameObject; RectTransform = rectTransform; _image = image; _layoutElement = layoutElement; _qualityText = qualityText; _valueText = valueText; } public static HoverFoodIcon Create(RectTransform parent, int index) { //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Unknown result type (might be due to invalid IL or missing references) //IL_00c0: Unknown result type (might be due to invalid IL or missing references) //IL_00cf: Unknown result type (might be due to invalid IL or missing references) //IL_00ed: Unknown result type (might be due to invalid IL or missing references) //IL_0104: Expected O, but got Unknown GameObject val = new GameObject($"FeedLikeGrandmaIcon{index}", new Type[4] { typeof(RectTransform), typeof(CanvasRenderer), typeof(Image), typeof(LayoutElement) }); RectTransform component = val.GetComponent(); ((Transform)component).SetParent((Transform)(object)parent, false); Image component2 = val.GetComponent(); component2.preserveAspect = true; ((Graphic)component2).raycastTarget = false; LayoutElement component3 = val.GetComponent(); component3.flexibleWidth = 0f; component3.flexibleHeight = 0f; TextMeshProUGUI qualityText = CreateBadgeText(component, "Quality", new Vector2(0.45f, 0.55f), Vector2.one, (TextAlignmentOptions)260, Color.white); TextMeshProUGUI valueText = CreateBadgeText(component, "Value", Vector2.zero, new Vector2(1f, 0.45f), (TextAlignmentOptions)1028, new Color(1f, 0.82f, 0.18f, 1f)); return new HoverFoodIcon(val, component, component2, component3, qualityText, valueText); } public void SetFood(ResolvedFoodEntry food, float iconSize) { //IL_0097: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Unknown result type (might be due to invalid IL or missing references) if (_lastSprite != food.Icon || _lastRequiredQuality != food.RequiredQuality || !Approximately(_lastValue, food.Value) || !Approximately(_lastIconSize, iconSize)) { _layoutElement.minWidth = iconSize; _layoutElement.minHeight = iconSize; _layoutElement.preferredWidth = iconSize; _layoutElement.preferredHeight = iconSize; RectTransform.sizeDelta = Vector2.one * iconSize; _image.sprite = food.Icon; ((Graphic)_image).color = Color.white; float maxSize = Mathf.Clamp(iconSize * 0.34f, 8f, 18f); ConfigureBadgeSize(_qualityText, maxSize); ConfigureBadgeSize(_valueText, maxSize); ((Component)_qualityText).gameObject.SetActive(food.RequiredQuality.HasValue); ((TMP_Text)_qualityText).text = (food.RequiredQuality.HasValue ? food.RequiredQuality.Value.ToString(CultureInfo.InvariantCulture) : string.Empty); ((TMP_Text)_valueText).text = FormatFoodValue(food.Value); _lastSprite = food.Icon; _lastRequiredQuality = food.RequiredQuality; _lastValue = food.Value; _lastIconSize = iconSize; } } private static TextMeshProUGUI CreateBadgeText(RectTransform parent, string name, Vector2 anchorMin, Vector2 anchorMax, TextAlignmentOptions alignment, Color color) { //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Expected O, but got Unknown //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_0105: Unknown result type (might be due to invalid IL or missing references) //IL_0119: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject("FeedLikeGrandma" + name + "Badge", new Type[2] { typeof(RectTransform), typeof(CanvasRenderer) }); val.SetActive(false); RectTransform component = val.GetComponent(); ((Transform)component).SetParent((Transform)(object)parent, false); component.anchorMin = anchorMin; component.anchorMax = anchorMax; component.offsetMin = new Vector2(1f, 0f); component.offsetMax = new Vector2(-2f, 0f); TextMeshProUGUI obj = val.AddComponent(); ((TMP_Text)obj).alignment = alignment; ((Graphic)obj).color = color; ((TMP_Text)obj).enableAutoSizing = true; ((TMP_Text)obj).fontSizeMin = 6f; ((TMP_Text)obj).fontSizeMax = 18f; ((TMP_Text)obj).fontStyle = (FontStyles)1; ((TMP_Text)obj).textWrappingMode = (TextWrappingModes)0; ((Graphic)obj).raycastTarget = false; ApplyHudFont((TMP_Text)(object)obj, (TMP_Text?)(object)(((Object)(object)Hud.instance != (Object)null) ? Hud.instance.m_hoverName : null)); Shadow obj2 = val.AddComponent(); obj2.effectColor = new Color(0f, 0f, 0f, 0.9f); obj2.effectDistance = new Vector2(1f, -1f); val.SetActive(true); return obj; } private static void ConfigureBadgeSize(TextMeshProUGUI text, float maxSize) { ((TMP_Text)text).fontSizeMax = maxSize; ((TMP_Text)text).fontSize = maxSize; } private static string FormatFoodValue(float value) { if (!(Mathf.Abs(value - Mathf.Round(value)) < 0.001f)) { return value.ToString("0.#", CultureInfo.InvariantCulture); } return Mathf.RoundToInt(value).ToString(CultureInfo.InvariantCulture); } } private sealed class PendingFeedRequest { public int RequestId { get; init; } public string ItemPrefabName { get; init; } = string.Empty; public int ItemQuality { get; init; } = 1; public ZDOID TargetId { get; init; } public long ExpectedResponderId { get; init; } public int RequestedAmount { get; init; } public long CreatedAtTicks { get; init; } } private sealed class FeedEffectPlan { public int Amount { get; init; } public int TameDeltaRequested { get; init; } public int GrowthDeltaRequested { get; init; } public int StarDeltaRequested { get; init; } public float HealPercentRequested { get; init; } public float FedDeltaRequested { get; init; } public float FedDeltaPossible { get; init; } public bool HasAnyEffect { get { if (TameDeltaRequested <= 0 && GrowthDeltaRequested <= 0 && StarDeltaRequested <= 0 && !(HealPercentRequested > 0f)) { return FedDeltaPossible > 0f; } return true; } } } private readonly struct FeedTargetContext { public Character Character { get; } public Tameable? Tameable { get; } public Growup? Growup { get; } public BaseAI? BaseAI { get; } public ZNetView NView { get; } public ResolvedFeedingRule Rule { get; } public FeedTargetContext(Character character, Tameable? tameable, Growup? growup, BaseAI? baseAI, ZNetView nview, ResolvedFeedingRule rule) { Character = character; Tameable = tameable; Growup = growup; BaseAI = baseAI; NView = nview; Rule = rule; } } private sealed class FeedRequestMessage { public int RequestId { get; init; } public ZDOID RequesterId { get; init; } public string ItemPrefabName { get; init; } = string.Empty; public int ItemQuality { get; init; } = 1; public int RequestedAmount { get; init; } public ZPackage Write() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Expected O, but got Unknown ZPackage val = new ZPackage(); val.Write(RequestId); val.Write(RequesterId); val.Write(ItemPrefabName); val.Write(ItemQuality); val.Write(RequestedAmount); return val; } public static bool TryRead(ZPackage pkg, out FeedRequestMessage? message) { //IL_0017: Unknown result type (might be due to invalid IL or missing references) message = null; try { message = new FeedRequestMessage { RequestId = pkg.ReadInt(), RequesterId = pkg.ReadZDOID(), ItemPrefabName = pkg.ReadString(), ItemQuality = pkg.ReadInt(), RequestedAmount = pkg.ReadInt() }; return true; } catch (Exception) { return false; } } } private sealed class FeedResponseMessage { public int RequestId { get; init; } public bool Success { get; set; } public int ConsumedAmount { get; set; } public string ItemPrefabName { get; init; } = string.Empty; public int TameDelta { get; set; } public int GrowthDelta { get; set; } public int StarDelta { get; set; } public float FedDelta { get; set; } public float HealPercent { get; set; } public ZDOID TargetId { get; set; } public Vector3 DisplayPosition { get; set; } public string Message { get; set; } = string.Empty; public ZPackage Write() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Expected O, but got Unknown ZPackage val = new ZPackage(); val.Write(RequestId); val.Write(Success); val.Write(ConsumedAmount); val.Write(ItemPrefabName); val.Write(TameDelta); val.Write(GrowthDelta); val.Write(StarDelta); val.Write(FedDelta); val.Write(HealPercent); val.Write(TargetId); val.Write(DisplayPosition); val.Write(Message); return val; } public static bool TryRead(ZPackage pkg, out FeedResponseMessage? message) { //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) message = null; try { message = new FeedResponseMessage { RequestId = pkg.ReadInt(), Success = pkg.ReadBool(), ConsumedAmount = pkg.ReadInt(), ItemPrefabName = pkg.ReadString(), TameDelta = pkg.ReadInt(), GrowthDelta = pkg.ReadInt(), StarDelta = pkg.ReadInt(), FedDelta = pkg.ReadSingle(), HealPercent = pkg.ReadSingle(), TargetId = pkg.ReadZDOID(), DisplayPosition = pkg.ReadVector3(), Message = pkg.ReadString() }; return true; } catch (Exception) { return false; } } } private static readonly int StarProgressHash = StringExtensionMethods.GetStableHashCode("flg_star_progress"); private static readonly int LevelScaledTameTotalHash = StringExtensionMethods.GetStableHashCode("flg_level_scaled_tame_total"); private static readonly int LevelScaledTameBaseHash = StringExtensionMethods.GetStableHashCode("flg_level_scaled_tame_base"); private static readonly int LevelScaledTameFactorHash = StringExtensionMethods.GetStableHashCode("flg_level_scaled_tame_factor"); private static readonly int LevelScaledTameLevelHash = StringExtensionMethods.GetStableHashCode("flg_level_scaled_tame_level"); private static readonly int GrowthElapsedHash = StringExtensionMethods.GetStableHashCode("flg_growth_elapsed"); private static readonly int GrowthLastEvalHash = StringExtensionMethods.GetStableHashCode("flg_growth_last_eval"); private static readonly int FeedCooldownHash = StringExtensionMethods.GetStableHashCode("flg_feed_cooldown"); private const float MeadBoostRange = 60f; private const float HoverFoodIconSpacing = 6f; private const float HoverFoodIconOffsetY = 6f; private const string FeedRequestRpc = "FLG_FeedRequest"; private const string FeedResponseRpc = "FLG_FeedResponse"; private const string TamedProgressionEffectPrefab = "fx_creature_tamed"; private const long PendingRequestTimeoutTicks = 300000000L; private static readonly Dictionary RawRules = new Dictionary(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary FoodNumbers = new Dictionary(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary ResolvedRules = new Dictionary(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary PendingRequests = new Dictionary(); private static readonly HashSet ProcreationInstancePrefabHashes = new HashSet(); private static readonly Dictionary> ProcreationInstanceCache = new Dictionary>(); private static readonly HashSet RegisteredTargetViews = new HashSet(); private static readonly HashSet RegisteredPlayerViews = new HashSet(); private static readonly List NearbyTamingBoostPlayers = new List(); private static TMP_FontAsset? CachedHudFont; private static readonly HashSet ExcludedPrefabNames = new HashSet(StringComparer.OrdinalIgnoreCase) { "Placeable_HardRock", "Player" }; private static HoverFoodIconHud? _hoverHud; private static RawFeedValues FeedDefaults = new RawFeedValues(); private static int _nextRequestId = 1; private static int _hoverTextTargetId; private static int _hoverTextTimeBucket = -1; private static string _hoverTextOriginal = string.Empty; private static string _hoverTextLanguage = string.Empty; private static string _hoverTextResult = string.Empty; internal static IReadOnlyDictionary Rules => ResolvedRules; internal static RawFeedValues CurrentDefaults => FeedDefaults; internal static IReadOnlyDictionary CurrentFoodNumbers => FoodNumbers; private static void ApplyHudFont(TMP_Text target, TMP_Text? preferred = null) { if ((Object)(object)preferred != (Object)null && (Object)(object)preferred.font != (Object)null) { target.font = preferred.font; if ((Object)(object)preferred.fontSharedMaterial != (Object)null) { target.fontSharedMaterial = preferred.fontSharedMaterial; } } else { TMP_FontAsset val = ResolveHudFont(); if ((Object)(object)val != (Object)null) { target.font = val; } } } private static TMP_FontAsset? ResolveHudFont() { if ((Object)(object)CachedHudFont != (Object)null) { return CachedHudFont; } if ((Object)(object)Hud.instance != (Object)null && (Object)(object)Hud.instance.m_hoverName != (Object)null && (Object)(object)((TMP_Text)Hud.instance.m_hoverName).font != (Object)null) { return CachedHudFont = ((TMP_Text)Hud.instance.m_hoverName).font; } TMP_FontAsset[] array = Resources.FindObjectsOfTypeAll(); foreach (TMP_FontAsset val in array) { if ((Object)(object)val != (Object)null && ((Object)val).name == "Valheim-AveriaSansLibre") { return CachedHudFont = val; } } if ((Object)(object)TMP_Settings.defaultFontAsset != (Object)null) { return CachedHudFont = TMP_Settings.defaultFontAsset; } TMP_Text[] array2 = Resources.FindObjectsOfTypeAll(); foreach (TMP_Text val2 in array2) { if ((Object)(object)val2 != (Object)null && (Object)(object)val2.font != (Object)null) { return CachedHudFont = val2.font; } } array = Resources.FindObjectsOfTypeAll(); foreach (TMP_FontAsset val3 in array) { if ((Object)(object)val3 != (Object)null) { return CachedHudFont = val3; } } return null; } internal static void Shutdown() { _hoverHud?.Destroy(); _hoverHud = null; RawRules.Clear(); FoodNumbers.Clear(); ResolvedRules.Clear(); PendingRequests.Clear(); ProcreationInstancePrefabHashes.Clear(); ProcreationInstanceCache.Clear(); RegisteredTargetViews.Clear(); RegisteredPlayerViews.Clear(); NearbyTamingBoostPlayers.Clear(); CachedHudFont = null; FeedDefaults = new RawFeedValues(); _nextRequestId = 1; InvalidateHoverTextCache(); } internal static void LoadYamlText(string yamlText, string source) { List list = new List(); RawFeedConfig rawFeedConfig; try { rawFeedConfig = FeedingYamlParser.Parse(yamlText, list); } catch (Exception arg) { FeedLikeGrandmaPlugin.Log.LogError((object)$"Failed to parse YAML from {source}: {arg}"); return; } RawRules.Clear(); FoodNumbers.Clear(); FeedDefaults = rawFeedConfig.Defaults; foreach (KeyValuePair foodNumber in rawFeedConfig.FoodNumbers) { FoodNumbers[foodNumber.Key] = foodNumber.Value; } foreach (KeyValuePair rule in rawFeedConfig.Rules) { RawRules[rule.Key] = rule.Value; } FeedLikeGrandmaPlugin.Log.LogInfo((object)$"Loaded {RawRules.Count} feed rule(s) and {FoodNumbers.Count} global food number(s) from {source}."); foreach (string item in list) { FeedLikeGrandmaPlugin.Log.LogWarning((object)item); } RebuildRuntimeData(); } internal static void RebuildRuntimeData() { InvalidateHoverTextCache(); ResolvedRules.Clear(); ProcreationInstancePrefabHashes.Clear(); ProcreationInstanceCache.Clear(); if (ObjectDB.instance == null || ZNetScene.instance == null) { return; } FeedExternalReferenceSupport.RefreshSnapshot(); List list = EnumerateLoadedPrefabs().ToList(); FeedReferenceWriter.CaptureMissingReferenceSnapshots(list); FeedReferenceWriter.RestoreReferenceSnapshots(list); foreach (KeyValuePair rawRule2 in RawRules) { GameObject val = FindLoadedPrefab(rawRule2.Key, list); if (val != null && !ShouldIgnorePrefab(val)) { ApplyConfiguredPrefabData(val, rawRule2.Value); } } ApplyInheritedConsumeItemsForLoadedPrefabs(); foreach (KeyValuePair rawRule3 in RawRules) { string key = rawRule3.Key; RawFeedingRule value = rawRule3.Value; GameObject val2 = FindLoadedPrefab(key, list); ResolvedFeedingRule resolvedRule; if (val2 == null) { FeedLikeGrandmaPlugin.Log.LogWarning((object)("Configured prefab '" + key + "' was not found in loaded prefabs.")); } else if (!ShouldIgnorePrefab(val2) && TryBuildResolvedRule(key, val2, value, explicitRule: true, out resolvedRule) && resolvedRule != null) { ResolvedRules[key] = resolvedRule; } } foreach (GameObject item in list) { string text = NormalizePrefabName(((Object)item).name); if (!ResolvedRules.ContainsKey(text) && HasImplicitFeedRule(item)) { RawFeedingRule rawRule = new RawFeedingRule { PrefabName = text }; if (TryBuildResolvedRule(text, item, rawRule, explicitRule: false, out ResolvedFeedingRule resolvedRule2) && resolvedRule2 != null) { ResolvedRules[text] = resolvedRule2; } } } FeedDragonFlightRestrictionSystem.OnRulesChanged(); FeedLikeGrandmaPlugin.RefreshGeneratedFilesIfNeeded(); } internal static void RegisterSpawnedInstance(ZNetView? nview) { if (ProcreationInstancePrefabHashes.Count != 0 && TryGetSpawnedInstancePrefabHash(nview, out var prefabHash) && ProcreationInstancePrefabHashes.Contains(prefabHash)) { if (!ProcreationInstanceCache.TryGetValue(prefabHash, out HashSet value)) { value = new HashSet(); ProcreationInstanceCache[prefabHash] = value; } value.Add(nview); } } internal static void DeregisterSpawnedInstance(ZNetView? nview) { if (nview == null) { return; } int instanceID = ((Object)nview).GetInstanceID(); RegisteredTargetViews.Remove(instanceID); RegisteredPlayerViews.Remove(instanceID); if (ProcreationInstanceCache.Count == 0) { return; } if (TryGetSpawnedInstancePrefabHash(nview, out var prefabHash) && ProcreationInstanceCache.TryGetValue(prefabHash, out HashSet value)) { value.Remove(nview); return; } foreach (HashSet value2 in ProcreationInstanceCache.Values) { value2.Remove(nview); } } private static GameObject? FindLoadedPrefab(string prefabName, IEnumerable loadedPrefabs) { string normalizedName = NormalizePrefabName(prefabName); ZNetScene instance = ZNetScene.instance; GameObject val = ((instance != null) ? instance.GetPrefab(normalizedName) : null); if (val != null) { return val; } return loadedPrefabs.FirstOrDefault((Func)((GameObject candidate) => candidate != null && NormalizePrefabName(((Object)candidate).name).Equals(normalizedName, StringComparison.OrdinalIgnoreCase))); } private static bool TryBuildResolvedRule(string prefabName, GameObject prefab, RawFeedingRule rawRule, bool explicitRule, out ResolvedFeedingRule? resolvedRule) { resolvedRule = null; Tameable component = prefab.GetComponent(); Growup component2 = prefab.GetComponent(); EggGrow component3 = prefab.GetComponent(); if (component == null && component2 == null) { if (explicitRule) { if (IsEggGrowOnlyConfigRule(rawRule, component3)) { return false; } FeedLikeGrandmaPlugin.Log.LogWarning((object)("Configured prefab '" + prefabName + "' is neither Tameable nor Growup. Skipping.")); } return false; } List effectiveFoodEntries = GetEffectiveFoodEntries(prefab, rawRule); HashSet hashSet = CollectValidFoodNames(prefab, new HashSet(StringComparer.OrdinalIgnoreCase)); hashSet.UnionWith(effectiveFoodEntries.Select((RawFoodEntry food) => food.PrefabName)); RawFeedValues rawFeedValues = rawRule.Feed ?? FeedDefaults; List list = ResolveFoodEntries(prefabName, rawRule, effectiveFoodEntries, hashSet, explicitRule, validateNativeFood: true); Dictionary> dictionary = ResolveLevelUpItems(prefabName, rawRule, explicitRule); ResolvedMountStaminaValues resolvedMountStaminaValues = ResolveMountStamina(rawRule.MountStamina); List restrictedBiomes = ResolveRestrictedBiomes(rawRule); if (!explicitRule && list.Count == 0 && dictionary.Count == 0 && resolvedMountStaminaValues == null && !rawRule.HasRestrictedBiomeOverride) { return false; } resolvedRule = new ResolvedFeedingRule { Prefab = prefab, LevelUpConstant = Math.Max(0f, rawFeedValues.LevelUpConstant), TamingConstant = Math.Max(0f, rawFeedValues.TamingConstant), GrowthConstant = Math.Max(0f, rawFeedValues.GrowthConstant), HealConstant = Math.Max(0f, rawFeedValues.HealConstant), FedConstant = Math.Max(0f, rawFeedValues.FedConstant), Foods = list, LevelUpItems = dictionary, MountStamina = resolvedMountStaminaValues, HasRestrictedBiomeOverride = rawRule.HasRestrictedBiomeOverride, RestrictedBiomes = restrictedBiomes }; return true; } private static List ResolveRestrictedBiomes(RawFeedingRule rawRule) { if (!rawRule.HasRestrictedBiomeOverride || rawRule.RestrictedBiomes.Count == 0) { return new List(); } return (from value in rawRule.RestrictedBiomes select value.Trim() into value where value.Length > 0 select value).Distinct(StringComparer.OrdinalIgnoreCase).ToList(); } private static ResolvedMountStaminaValues? ResolveMountStamina(RawMountStaminaValues? rawMountStamina) { if (rawMountStamina == null) { return null; } return new ResolvedMountStaminaValues { AttackStaminaCost = Mathf.Max(0f, rawMountStamina.AttackStaminaCost), JumpStaminaCost = Mathf.Max(0f, rawMountStamina.JumpStaminaCost), AttackCooldown = Mathf.Max(0f, rawMountStamina.AttackCooldown) }; } private static Dictionary> ResolveLevelUpItems(string prefabName, RawFeedingRule rawRule, bool explicitRule) { Dictionary> dictionary = new Dictionary>(); foreach (KeyValuePair> levelUpItem in rawRule.LevelUpItems) { List value = ResolveFoodEntries(prefabName, rawRule, levelUpItem.Value, null, explicitRule, validateNativeFood: false); dictionary[Math.Max(1, levelUpItem.Key)] = value; } return dictionary; } private static List ResolveFoodEntries(string prefabName, RawFeedingRule rawRule, IEnumerable rawFoods, HashSet? validFoodNames, bool explicitRule, bool validateNativeFood) { List list = new List(); foreach (RawFoodEntry rawFood in rawFoods) { string text = FeedingYamlParser.FormatFoodKey(rawFood.PrefabName, rawFood.RequiredQuality); if (!TryResolveFoodValue(rawRule, rawFood, out var value)) { if (explicitRule) { FeedLikeGrandmaPlugin.Log.LogWarning((object)("Ignoring food '" + text + "' for prefab '" + prefabName + "' because no food number was configured.")); } continue; } if (validateNativeFood && validFoodNames != null && !validFoodNames.Contains(rawFood.PrefabName)) { if (explicitRule) { FeedLikeGrandmaPlugin.Log.LogWarning((object)("Ignoring food '" + text + "' for prefab '" + prefabName + "' because it is not valid native food for that creature chain.")); } continue; } GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(rawFood.PrefabName); if (itemPrefab == null) { if (explicitRule) { FeedLikeGrandmaPlugin.Log.LogWarning((object)("Configured food '" + text + "' for prefab '" + prefabName + "' was not found in ObjectDB.")); } continue; } ItemDrop component = itemPrefab.GetComponent(); if (component == null) { if (explicitRule) { FeedLikeGrandmaPlugin.Log.LogWarning((object)("Configured food '" + text + "' for prefab '" + prefabName + "' is not an item prefab.")); } } else { list.Add(new ResolvedFoodEntry { PrefabName = ((Object)itemPrefab).name, RequiredQuality = rawFood.RequiredQuality, Value = value, Icon = component.m_itemData.GetIcon() }); } } return list; } private static bool HasImplicitFeedRule(GameObject prefab) { if (prefab.GetComponent() == null && prefab.GetComponent() == null) { return false; } return ResolveConsumeData(prefab).ConsumeItems.Count > 0; } private static int GetMaxAllowedLevel(FeedTargetContext context) { if (FeedLikeGrandmaPlugin.LevelUpLimitMode.Value == FeedLikeGrandmaPlugin.LevelUpLimit.Level) { return Mathf.Max(1, FeedLikeGrandmaPlugin.LevelUpMaxLevel.Value); } if (!IsVanillaLevelOneLockedPrefab(NormalizePrefabName(((Object)((Component)context.Character).gameObject).name))) { return 3; } return 1; } private static bool CanGainLevel(FeedTargetContext context) { return Mathf.Max(1, context.Character.GetLevel()) < GetMaxAllowedLevel(context); } private static bool IsVanillaLevelOneLockedPrefab(string prefabName) { if (!prefabName.Equals("Lox", StringComparison.OrdinalIgnoreCase)) { return prefabName.Equals("Hen", StringComparison.OrdinalIgnoreCase); } return true; } private static IEnumerable EnumerateLoadedPrefabs() { if (ZNetScene.instance == null || ZNetScene.instance.m_prefabs == null) { yield break; } HashSet seen = new HashSet(StringComparer.OrdinalIgnoreCase); IEnumerable enumerable = ZNetScene.instance.m_prefabs.Concat(FeedExternalReferenceSupport.GetExternalPrefabs()); foreach (GameObject item2 in enumerable) { if (item2 != null && !string.IsNullOrWhiteSpace(((Object)item2).name)) { string item = NormalizePrefabName(((Object)item2).name); if (seen.Add(item) && !ShouldIgnorePrefab(item2)) { yield return item2; } } } } internal static bool ShouldIgnorePrefab(GameObject? prefab) { //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Invalid comparison between Unknown and I4 //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Invalid comparison between Unknown and I4 if (prefab == null) { return true; } string item = NormalizePrefabName(((Object)prefab).name); if (ExcludedPrefabNames.Contains(item)) { return true; } Character component = prefab.GetComponent(); if (component != null) { if ((int)component.m_faction != 11) { return (int)component.m_faction == 0; } return true; } return false; } internal static FeedConsumeData ResolveConsumeData(GameObject prefab) { List list = ResolveOwnConsumeItems(prefab); bool flag = list.Count > 0; FeedConsumeData feedConsumeData = new FeedConsumeData { ConsumeItems = list }; if (!TryFindGrowupConsumeSource(prefab, new HashSet(StringComparer.OrdinalIgnoreCase), out GameObject sourcePrefab)) { return feedConsumeData; } List list2 = (List)(((Object)(object)sourcePrefab == (Object)null) ? ((IList)new List()) : ((IList)ResolveOwnConsumeItems(sourcePrefab))); bool flag2 = list2.Count > 0; bool flag3 = !flag && flag2; if (!flag3) { return feedConsumeData; } return new FeedConsumeData { InheritedConsumeItems = flag3, ConsumeItems = (List)(flag3 ? ((IList)list2) : ((IList)feedConsumeData.ConsumeItems)) }; } private static List ResolveOwnConsumeItems(GameObject prefab) { List consumeItems = GetConsumeItems(prefab.GetComponent()); if (consumeItems.Count <= 0) { return FeedExternalReferenceSupport.ResolveConfiguredConsumeItems(prefab); } return consumeItems; } private static bool HasConsumeItems(MonsterAI? monsterAI) { if (monsterAI != null && monsterAI.m_consumeItems != null) { return monsterAI.m_consumeItems.Any((ItemDrop item) => item != null); } return false; } private static List GetConsumeItems(MonsterAI? monsterAI) { if (monsterAI == null || monsterAI.m_consumeItems == null) { return new List(); } return monsterAI.m_consumeItems.Where((ItemDrop item) => item != null).ToList(); } private static bool TryFindGrowupConsumeSource(GameObject prefab, HashSet visited, out GameObject? sourcePrefab) { sourcePrefab = null; if (prefab == null || !visited.Add(((Object)prefab).name)) { return false; } foreach (GameObject item in EnumerateGrowthTargets(prefab.GetComponent())) { if (item != null && !visited.Contains(((Object)item).name)) { if (ResolveOwnConsumeItems(item).Count > 0) { sourcePrefab = item; return true; } if (TryFindGrowupConsumeSource(item, visited, out sourcePrefab)) { return true; } } } return false; } private static void ApplyInheritedConsumeItemsForLoadedPrefabs() { foreach (GameObject item in EnumerateLoadedPrefabs()) { MonsterAI component = item.GetComponent(); if (component != null && !HasConsumeItems(component)) { FeedConsumeData feedConsumeData = ResolveConsumeData(item); if (feedConsumeData.InheritedConsumeItems) { component.m_consumeItems = feedConsumeData.ConsumeItems.ToList(); } } } } private static List GetEffectiveFoodEntries(GameObject prefab, RawFeedingRule rawRule) { List list = rawRule.Foods.ToList(); if (list.Count > 0 || rawRule.ConsumeItems.Count > 0) { return list; } FeedConsumeData feedConsumeData = ResolveConsumeData(prefab); if (feedConsumeData.ConsumeItems.Count == 0) { return list; } foreach (ItemDrop consumeItem in feedConsumeData.ConsumeItems) { string itemName = FeedExternalReferenceSupport.ExtractItemDropPrefabName(consumeItem); if (!list.Any((RawFoodEntry food) => food.PrefabName.Equals(itemName, StringComparison.OrdinalIgnoreCase))) { list.Add(new RawFoodEntry { PrefabName = itemName, DefaultToOne = true }); } } return list; } private static void ApplyConfiguredPrefabData(GameObject prefab, RawFeedingRule rawRule) { ApplyConfiguredConsumeData(prefab, rawRule); ApplyConfiguredTameableData(prefab, rawRule); ApplyConfiguredProcreationData(prefab, rawRule); ApplyConfiguredGrowupData(prefab, rawRule); ApplyConfiguredEggGrowData(prefab, rawRule); } private static bool IsEggGrowOnlyConfigRule(RawFeedingRule rawRule, EggGrow? eggGrow) { if (eggGrow != null && rawRule.EggGrow != null && rawRule.Feed == null && rawRule.Tame == null && rawRule.Procreation == null && rawRule.Grow == null && rawRule.Foods.Count == 0 && rawRule.ConsumeItems.Count == 0) { return rawRule.LevelUpItems.Count == 0; } return false; } private static void ApplyConfiguredConsumeData(GameObject prefab, RawFeedingRule rawRule) { List list = rawRule.ConsumeItems.Where((string name) => !string.IsNullOrWhiteSpace(name)).Distinct(StringComparer.OrdinalIgnoreCase).ToList(); if (list.Count == 0) { return; } MonsterAI component = prefab.GetComponent(); if (component == null || list.Count <= 0) { return; } List list2 = new List(); foreach (string item in list) { if (TryResolveItemDrop(((Object)prefab).name, item, out ItemDrop itemDrop)) { list2.Add(itemDrop); } } component.m_consumeItems = list2; } private static void ApplyConfiguredTameableData(GameObject prefab, RawFeedingRule rawRule) { if (rawRule.Tame != null) { Tameable component = prefab.GetComponent(); if (component == null) { FeedLikeGrandmaPlugin.Log.LogWarning((object)("Configured prefab '" + ((Object)prefab).name + "' has tameable settings but no Tameable component.")); return; } component.m_fedDuration = rawRule.Tame.FedDuration; component.m_tamingTime = rawRule.Tame.TamingTime; component.m_startsTamed = rawRule.Tame.StartsTamed; component.m_commandable = rawRule.Tame.Commandable; } } private static void ApplyConfiguredProcreationData(GameObject prefab, RawFeedingRule rawRule) { if (rawRule.Procreation != null) { Procreation component = prefab.GetComponent(); if (component == null) { FeedLikeGrandmaPlugin.Log.LogWarning((object)("Configured prefab '" + ((Object)prefab).name + "' has procreation settings but no Procreation component.")); return; } component.m_maxCreatures = rawRule.Procreation.MaxCreatures; component.m_totalCheckRange = rawRule.Procreation.TotalCheckRange; } } private static void ApplyConfiguredGrowupData(GameObject prefab, RawFeedingRule rawRule) { if (rawRule.Grow != null) { Growup component = prefab.GetComponent(); if (component == null) { FeedLikeGrandmaPlugin.Log.LogWarning((object)("Configured prefab '" + ((Object)prefab).name + "' has grow settings but no Growup component.")); } else { component.m_growTime = rawRule.Grow.GrowTime; } } } private static void ApplyConfiguredEggGrowData(GameObject prefab, RawFeedingRule rawRule) { if (rawRule.EggGrow != null) { EggGrow component = prefab.GetComponent(); if (component == null) { FeedLikeGrandmaPlugin.Log.LogWarning((object)("Configured prefab '" + ((Object)prefab).name + "' has eggGrow settings but no EggGrow component.")); } else { component.m_growTime = rawRule.EggGrow.GrowTime; } } } internal static bool ShouldRunProcreation(Procreation procreation) { //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) if (FeedLikeGrandmaPlugin.HorizontalProcreationLimit.Value != FeedLikeGrandmaPlugin.Toggle.On) { return true; } if (procreation == null || procreation.m_nview == null || !procreation.m_nview.IsValid() || !procreation.m_nview.IsOwner() || procreation.m_tameable == null || !procreation.m_tameable.IsTamed() || procreation.IsPregnant()) { return true; } if (!TryEnsureProcreationPrefabs(procreation) || procreation.m_myPrefab == null || procreation.m_offspringPrefab == null) { return true; } Vector3 position = ((Component)procreation).transform.position; int num = CountPrefabInstancesXZ(procreation.m_myPrefab, position, procreation.m_totalCheckRange); int num2 = CountPrefabInstancesXZ(procreation.m_offspringPrefab, position, procreation.m_totalCheckRange); return num + num2 < procreation.m_maxCreatures; } private static bool TryEnsureProcreationPrefabs(Procreation procreation) { if (procreation.m_myPrefab != null && procreation.m_offspringPrefab != null) { return true; } if (ZNetScene.instance == null || procreation.m_nview == null || !procreation.m_nview.IsValid()) { return false; } if (procreation.m_offspringPrefab == null) { if (procreation.m_offspring == null) { return false; } string prefabName = Utils.GetPrefabName(procreation.m_offspring); procreation.m_offspringPrefab = ZNetScene.instance.GetPrefab(prefabName); } if (procreation.m_myPrefab == null) { int prefab = procreation.m_nview.GetZDO().GetPrefab(); procreation.m_myPrefab = ZNetScene.instance.GetPrefab(prefab); } if (procreation.m_myPrefab != null) { return procreation.m_offspringPrefab != null; } return false; } private static int CountPrefabInstancesXZ(GameObject prefab, Vector3 center, float maxRange) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) int prefabHash = GetPrefabHash(prefab); if (prefabHash == 0) { return 0; } EnsureProcreationInstanceInterest(prefabHash); return CountCachedPrefabInstancesXZ(prefabHash, center, maxRange); } private static int CountCachedPrefabInstancesXZ(int prefabHash, Vector3 center, float maxRange) { //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) if (!ProcreationInstanceCache.TryGetValue(prefabHash, out HashSet value) || value.Count == 0) { return 0; } value.RemoveWhere((ZNetView view) => !IsCachedProcreationInstanceValid(view, prefabHash)); int num = 0; foreach (ZNetView item in value) { if (IsWithinXZRange(((Component)item).transform.position, center, maxRange)) { num++; } } return num; } private static void EnsureProcreationInstanceInterest(int prefabHash) { if (prefabHash != 0 && ProcreationInstancePrefabHashes.Add(prefabHash)) { RebuildProcreationInstanceBucket(prefabHash); } } private static void RebuildProcreationInstanceBucket(int prefabHash) { HashSet hashSet = new HashSet(); if (ZNetScene.instance != null && ZNetScene.instance.m_instances != null) { foreach (ZNetView value in ZNetScene.instance.m_instances.Values) { if (IsCachedProcreationInstanceValid(value, prefabHash)) { hashSet.Add(value); } } } ProcreationInstanceCache[prefabHash] = hashSet; } private static bool IsCachedProcreationInstanceValid(ZNetView? nview, int prefabHash) { if (TryGetSpawnedInstancePrefabHash(nview, out var prefabHash2)) { return prefabHash2 == prefabHash; } return false; } private static bool TryGetSpawnedInstancePrefabHash(ZNetView? nview, out int prefabHash) { prefabHash = 0; if ((Object)(object)nview == (Object)null || !nview.IsValid()) { return false; } ZDO zDO = nview.GetZDO(); if (zDO == null) { return false; } prefabHash = zDO.GetPrefab(); return prefabHash != 0; } private static int GetPrefabHash(GameObject prefab) { if (prefab == null) { return 0; } string text = NormalizePrefabName(((Object)prefab).name); if (!string.IsNullOrWhiteSpace(text)) { return StringExtensionMethods.GetStableHashCode(text); } return 0; } private static bool IsWithinXZRange(Vector3 position, Vector3 center, float maxRange) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) if (!(maxRange <= 0f)) { return Utils.DistanceXZ(position, center) <= maxRange; } return true; } private static bool TryResolveItemDrop(string creaturePrefabName, string itemPrefabName, out ItemDrop? itemDrop) { itemDrop = null; GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(itemPrefabName); if (itemPrefab == null) { FeedLikeGrandmaPlugin.Log.LogWarning((object)("Configured consume item '" + itemPrefabName + "' for prefab '" + creaturePrefabName + "' was not found in ObjectDB.")); return false; } itemDrop = itemPrefab.GetComponent(); if (itemDrop == null) { FeedLikeGrandmaPlugin.Log.LogWarning((object)("Configured consume item '" + itemPrefabName + "' for prefab '" + creaturePrefabName + "' is not an item prefab.")); return false; } return true; } private static bool TryResolveFoodValue(RawFeedingRule rule, RawFoodEntry food, out float value) { if (food.Value.HasValue) { value = food.Value.Value; return true; } if (food.RequiredQuality.HasValue && FoodNumbers.TryGetValue(FeedingYamlParser.FormatFoodKey(food.PrefabName, food.RequiredQuality), out value)) { return true; } if (FoodNumbers.TryGetValue(food.PrefabName, out value)) { return true; } if (food.DefaultToOne) { value = 1f; return true; } value = 0f; return false; } internal static void RegisterTargetRpc(ZNetView? nview) { if (nview == null || !nview.IsValid()) { return; } int instanceID = ((Object)nview).GetInstanceID(); if (RegisteredTargetViews.Add(instanceID)) { nview.Register("FLG_FeedRequest", (Action)delegate(long sender, ZPackage pkg) { ReceiveFeedRequest(nview, sender, pkg); }); } } internal static void RegisterPlayerRpc(Player? player) { if (player == null || ((Character)player).m_nview == null || !((Character)player).m_nview.IsValid()) { return; } int instanceID = ((Object)((Character)player).m_nview).GetInstanceID(); if (RegisteredPlayerViews.Add(instanceID)) { ((Character)player).m_nview.Register("FLG_FeedResponse", (Action)delegate(long sender, ZPackage pkg) { ReceiveFeedResponse(player, sender, pkg); }); } } internal static bool TryHandleTameableUseItem(Tameable tameable, Humanoid user, ItemData item) { if (tameable != null) { Player val = (Player)(object)((user is Player) ? user : null); if (val != null && item != null) { if (tameable.m_saddleItem != null && (Object)(object)item.m_dropPrefab != (Object)null && ((Object)item.m_dropPrefab).name == ((Object)tameable.m_saddleItem).name) { return false; } return TryStartFeedRequest(val, ((Component)tameable).gameObject, item); } } return false; } internal static bool TryHandleGrowupUseItem(Humanoid user, GameObject? hoverObject, ItemData item) { Player val = (Player)(object)((user is Player) ? user : null); if (val == null || hoverObject == null || item == null) { return false; } Character componentInParent = hoverObject.GetComponentInParent(); if (componentInParent == null || ((Component)componentInParent).GetComponent() != null) { return false; } return TryStartFeedRequest(val, ((Component)componentInParent).gameObject, item); } internal static string AugmentHoverText(Character character, string originalText) { string text = originalText ?? string.Empty; int num = ((!((Object)(object)character == (Object)null)) ? ((Object)character).GetInstanceID() : 0); int num2 = Mathf.FloorToInt(Time.unscaledTime); string text2 = ((Localization.instance == null) ? string.Empty : Localization.instance.GetSelectedLanguage()); if (_hoverTextTargetId == num && _hoverTextTimeBucket == num2 && string.Equals(_hoverTextOriginal, text, StringComparison.Ordinal) && string.Equals(_hoverTextLanguage, text2, StringComparison.Ordinal)) { return _hoverTextResult; } if (!TryBuildContext(character, out var context)) { return RememberHoverText(num, num2, text, text2, text); } List list = new List(); if (context.Tameable != null && !context.Character.IsTamed()) { GetTameProgress(context, out var elapsed, out var total); list.Add("" + FeedLocalization.Format("$flg_hover_tame_progress", elapsed.ToString("0", CultureInfo.InvariantCulture), total.ToString("0", CultureInfo.InvariantCulture)) + ""); } if (context.Growup != null) { if (context.Character.IsTamed()) { float num3 = Mathf.Min(GetGrowthElapsedSeconds(context.Growup, context.Character, initializeIfMissing: false), context.Growup.m_growTime); list.Add("" + FeedLocalization.Format("$flg_hover_growth_progress", num3.ToString("0", CultureInfo.InvariantCulture), context.Growup.m_growTime.ToString("0", CultureInfo.InvariantCulture)) + ""); } else { list.Add("" + FeedLocalization.Text("$flg_hover_growth_locked_tamed") + ""); } } if (context.Tameable != null && context.Character.IsTamed()) { float maxFedSeconds; float fedRemainingSeconds = GetFedRemainingSeconds(context, out maxFedSeconds); if (maxFedSeconds > 0f) { list.Add("" + FeedLocalization.Format("$flg_hover_fed_progress", FormatDurationClock(fedRemainingSeconds), FormatDurationClock(maxFedSeconds)) + ""); } } if (CanUseTamingTimeRequirements(context)) { if (!context.Character.IsTamed()) { list.Add("" + FeedLocalization.Text("$flg_hover_stars_locked_tamed") + ""); } else if (context.Growup != null) { list.Add("" + FeedLocalization.Text("$flg_hover_stars_locked_growth") + ""); } else { int num4 = Mathf.Max(1, context.Character.GetLevel()); int maxAllowedLevel = GetMaxAllowedLevel(context); if (num4 >= maxAllowedLevel) { list.Add("" + FeedLocalization.Text("$flg_hover_level_max") + ""); } else { int targetLevel = num4 + 1; float starProgress = GetStarProgress(context.NView); float requiredStarProgress = GetRequiredStarProgress(context, targetLevel); list.Add("" + FeedLocalization.Format("$flg_hover_level_progress", num4.ToString(CultureInfo.InvariantCulture), targetLevel.ToString(CultureInfo.InvariantCulture), Mathf.Min(starProgress, requiredStarProgress).ToString("0", CultureInfo.InvariantCulture), requiredStarProgress.ToString("0", CultureInfo.InvariantCulture)) + ""); } } } if (list.Count == 0) { return RememberHoverText(num, num2, text, text2, text); } string result = ((text.Length == 0) ? context.Character.GetHoverName() : text) + "\n" + string.Join("\n", list); return RememberHoverText(num, num2, text, text2, result); } private static string RememberHoverText(int targetId, int timeBucket, string originalText, string language, string result) { _hoverTextTargetId = targetId; _hoverTextTimeBucket = timeBucket; _hoverTextOriginal = originalText; _hoverTextLanguage = language; _hoverTextResult = result; return result; } private static void InvalidateHoverTextCache() { _hoverTextTargetId = 0; _hoverTextTimeBucket = -1; _hoverTextOriginal = string.Empty; _hoverTextLanguage = string.Empty; _hoverTextResult = string.Empty; } internal static void UpdateHoverIcons(Player player) { if (_hoverHud == null) { if (Hud.instance == null) { return; } _hoverHud = new HoverFoodIconHud(); } if (!TryGetHoverFoods(player, out var rows)) { _hoverHud.Hide(); } else if (rows.TotalCount == 0) { _hoverHud.Hide(); } else { _hoverHud.Show(rows); } } internal static bool HasRuleFor(GameObject gameObject) { if (!ShouldIgnorePrefab(gameObject)) { return ResolvedRules.ContainsKey(NormalizePrefabName(((Object)gameObject).name)); } return false; } internal static bool TryGetMountStamina(Character character, out ResolvedMountStaminaValues? mountStamina) { mountStamina = null; if (character == null) { return false; } GameObject gameObject = ((Component)character).gameObject; if (ShouldIgnorePrefab(gameObject)) { return false; } string key = NormalizePrefabName(((Object)gameObject).name); if (!ResolvedRules.TryGetValue(key, out ResolvedFeedingRule value) || value.MountStamina == null) { return false; } mountStamina = value.MountStamina; return true; } internal static bool TryGetRestrictedBiomes(Character character, out IReadOnlyList restrictedBiomes) { restrictedBiomes = Array.Empty(); if (character == null) { return false; } GameObject gameObject = ((Component)character).gameObject; if (ShouldIgnorePrefab(gameObject)) { return false; } string key = NormalizePrefabName(((Object)gameObject).name); if (!ResolvedRules.TryGetValue(key, out ResolvedFeedingRule value) || !value.HasRestrictedBiomeOverride) { return false; } restrictedBiomes = value.RestrictedBiomes; return true; } internal static void ApplyConfiguredTamingMultiplier(Tameable tameable) { if (TryBuildContext(((Component)tameable).GetComponentInParent(), out var context) && context.Tameable != null) { context.Tameable.m_tamingBoostMultiplier = FeedLikeGrandmaPlugin.GetTamingBoostMultiplier(); EnsureLevelScaledTameTime(context); ApplyEffectiveFedDuration(context); } } internal static bool TryGetHungryState(Tameable tameable, out bool isHungry) { isHungry = false; Character character = ((tameable == null) ? null : ((Component)tameable).GetComponentInParent()); if (tameable == null || !TryBuildContext(character, out var context) || context.Tameable == null) { return false; } ApplyEffectiveFedDuration(context); float maxFedSeconds; float fedRemainingSeconds = GetFedRemainingSeconds(context, out maxFedSeconds); if (maxFedSeconds <= 0f) { return false; } isHungry = fedRemainingSeconds <= 0f; return true; } internal static bool TryCanAutoConsume(MonsterAI monsterAI, ItemData item, out bool canConsume) { bool matchesLevelUpFood; return TryGetAutoConsumeMatch(monsterAI, item, out canConsume, out matchesLevelUpFood); } internal static bool TryGetAutoConsumeMatch(MonsterAI monsterAI, ItemData item, out bool canConsume, out bool matchesLevelUpFood) { canConsume = false; matchesLevelUpFood = false; Character character = ((monsterAI == null) ? null : ((Component)monsterAI).GetComponentInParent()); if (monsterAI == null || item == null || !TryBuildContext(character, out var context)) { return false; } return TryGetAutoConsumeMatch(context, item, out canConsume, out matchesLevelUpFood); } internal static bool TrySelectAutoFeedItem(MonsterAI monsterAI, IEnumerable candidates, out ItemData? selectedItem) { selectedItem = null; Character character = ((monsterAI == null) ? null : ((Component)monsterAI).GetComponentInParent()); if (monsterAI == null || candidates == null || !TryBuildContext(character, out var context)) { return false; } ItemData val = null; foreach (ItemData candidate in candidates) { if (candidate != null && candidate.m_stack > 0 && TryGetAutoConsumeMatch(context, candidate, out var canConsume, out var matchesLevelUpFood) && canConsume) { if (matchesLevelUpFood) { selectedItem = candidate; return true; } if (val == null) { val = candidate; } } } selectedItem = val; return selectedItem != null; } internal static bool TryHandleAutomaticConsumedItem(Tameable tameable, ItemDrop item, out float targetHealth) { targetHealth = 0f; Character character = ((tameable == null) ? null : ((Component)tameable).GetComponentInParent()); if (tameable == null || item == null || !TryBuildContext(character, out var context) || context.Tameable == null || !context.NView.IsValid() || !context.NView.IsOwner() || !TryGetItemPrefabName(item.m_itemData, out string prefabName)) { return false; } int itemQuality = Mathf.Max(1, item.m_itemData.m_quality); if (!TryGetFeedEntriesForItem(context, prefabName, itemQuality, out ResolvedFoodEntry dailyFood, out ResolvedFoodEntry levelUpFood, out bool hasLevelUpItems)) { return false; } ApplyAutomaticFeedEffects(context, dailyFood, levelUpFood, hasLevelUpItems); targetHealth = context.Character.GetHealth(); return true; } internal static bool HandleGrowUpdate(Growup growup) { Character character = ((growup == null) ? null : ((Component)growup).GetComponentInParent()); if (growup == null || !TryBuildContext(character, out var context)) { return false; } if (!context.NView.IsValid() || !context.NView.IsOwner()) { return false; } if (AdvanceGrowth(context.Growup, context.Character) < context.Growup.m_growTime) { return false; } GrowNow(context.Growup, context.Character); return false; } private static bool TryStartFeedRequest(Player player, GameObject targetObject, ItemData item) { //IL_016c: Unknown result type (might be due to invalid IL or missing references) //IL_01b1: Unknown result type (might be due to invalid IL or missing references) if (!TryBuildContext(targetObject, out var context)) { return false; } if (!TryGetItemPrefabName(item, out string prefabName)) { return false; } int itemQuality = Mathf.Max(1, item.m_quality); bool num = FindMatchingFood(context.Rule.Foods, prefabName, itemQuality) != null; IReadOnlyList foods; bool flag = TryGetLevelUpItems(context, out foods) && foods != null && FindMatchingFood(foods, prefabName, itemQuality) != null; if (!num && !flag) { return false; } if (context.BaseAI != null && context.BaseAI.IsAlerted()) { FeedLocalization.Message(player, "$flg_feed_target_alerted", context.Character.GetHoverName()); return true; } long num2 = context.NView.GetZDO().GetLong(FeedCooldownHash, 0L); long ticks = ZNet.instance.GetTime().Ticks; if (num2 > ticks) { TimeSpan timeSpan = new TimeSpan(num2 - ticks); FeedLocalization.Message(player, "$flg_feed_cooldown", timeSpan.TotalSeconds.ToString("0.#", CultureInfo.InvariantCulture)); return true; } CleanupPendingRequests(ticks); int num3 = _nextRequestId++; int requestedAmount = Mathf.Min(Mathf.Max(1, item.m_stack), FeedLikeGrandmaPlugin.MaxItemsPerFeed.Value); PendingRequests[num3] = new PendingFeedRequest { RequestId = num3, ItemPrefabName = prefabName, ItemQuality = itemQuality, TargetId = context.NView.GetZDO().m_uid, ExpectedResponderId = context.NView.GetZDO().GetOwner(), RequestedAmount = requestedAmount, CreatedAtTicks = ticks }; FeedRequestMessage feedRequestMessage = new FeedRequestMessage { RequestId = num3, RequesterId = ((Character)player).GetZDOID(), ItemPrefabName = prefabName, ItemQuality = itemQuality, RequestedAmount = requestedAmount }; context.NView.InvokeRPC("FLG_FeedRequest", new object[1] { feedRequestMessage.Write() }); return true; } private static void CleanupPendingRequests(long nowTicks) { if (PendingRequests.Count == 0) { return; } List list = null; foreach (KeyValuePair pendingRequest in PendingRequests) { if (pendingRequest.Value.CreatedAtTicks <= 0 || nowTicks - pendingRequest.Value.CreatedAtTicks > 300000000) { if (list == null) { list = new List(); } list.Add(pendingRequest.Key); } } if (list == null) { return; } foreach (int item in list) { PendingRequests.Remove(item); } } private static void ReceiveFeedRequest(ZNetView nview, long sender, ZPackage pkg) { //IL_0032: Unknown result type (might be due to invalid IL or missing references) if (!nview.IsOwner()) { return; } if (!FeedRequestMessage.TryRead(pkg, out FeedRequestMessage message) || message == null) { FeedLikeGrandmaPlugin.Log.LogDebug((object)$"Ignored malformed feed request from peer {sender}."); return; } Player val = FindPlayer(message.RequesterId); if (((Character)(val?)).m_nview != null && ((Character)val).m_nview.IsValid()) { ZDO zDO = ((Character)val).m_nview.GetZDO(); if (sender == 0L || zDO == null || zDO.GetOwner() != sender) { FeedLikeGrandmaPlugin.Log.LogWarning((object)$"Ignored feed request {message.RequestId} whose sender does not own the requesting player."); return; } if (message.RequestId <= 0 || string.IsNullOrWhiteSpace(message.ItemPrefabName) || message.ItemPrefabName.Length > 256 || message.ItemQuality <= 0 || message.RequestedAmount <= 0) { FeedLikeGrandmaPlugin.Log.LogDebug((object)$"Ignored invalid feed request {message.RequestId} from peer {sender}."); return; } FeedResponseMessage feedResponseMessage = ProcessFeedRequest(((Component)nview).gameObject, message, val); ((Character)val).m_nview.InvokeRPC("FLG_FeedResponse", new object[1] { feedResponseMessage.Write() }); } } private static FeedResponseMessage ProcessFeedRequest(GameObject targetObject, FeedRequestMessage request, Player requester) { //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_02fd: Unknown result type (might be due to invalid IL or missing references) FeedResponseMessage feedResponseMessage = new FeedResponseMessage { RequestId = request.RequestId, ItemPrefabName = request.ItemPrefabName, TargetId = ZDOID.None, Message = string.Empty }; if (!TryBuildContext(targetObject, out var context)) { feedResponseMessage.Message = FeedLocalization.Text("$flg_feed_target_not_configured"); return feedResponseMessage; } feedResponseMessage.TargetId = context.NView.GetZDO().m_uid; feedResponseMessage.DisplayPosition = context.Character.GetTopPoint(); if (context.BaseAI != null && context.BaseAI.IsAlerted()) { feedResponseMessage.Message = FeedLocalization.Format("$flg_feed_target_alerted", context.Character.GetHoverName()); return feedResponseMessage; } int itemQuality = Mathf.Max(1, request.ItemQuality); if (!TryGetFeedEntriesForItem(context, request.ItemPrefabName, itemQuality, out ResolvedFoodEntry dailyFood, out ResolvedFoodEntry levelUpFood, out bool hasLevelUpItems)) { feedResponseMessage.Message = FeedLocalization.Text("$flg_feed_invalid_item"); return feedResponseMessage; } int num = CountItemsByPrefab(((Humanoid)requester).GetInventory(), request.ItemPrefabName, itemQuality); if (num <= 0) { feedResponseMessage.Message = FeedLocalization.Text("$flg_feed_no_matching_item"); return feedResponseMessage; } int num2 = Mathf.Min(new int[3] { request.RequestedAmount, num, FeedLikeGrandmaPlugin.MaxItemsPerFeed.Value }); if (num2 <= 0) { feedResponseMessage.Message = FeedLocalization.Text("$flg_feed_no_items_consumed"); return feedResponseMessage; } long ticks = ZNet.instance.GetTime().Ticks; long num3 = context.NView.GetZDO().GetLong(FeedCooldownHash, 0L); if (num3 > ticks) { TimeSpan timeSpan = new TimeSpan(num3 - ticks); feedResponseMessage.Message = FeedLocalization.Format("$flg_feed_cooldown", timeSpan.TotalSeconds.ToString("0.#", CultureInfo.InvariantCulture)); return feedResponseMessage; } float feedMultiplier = (((Character)requester).GetSEMan().HaveStatusAttribute((StatusAttribute)8) ? FeedLikeGrandmaPlugin.GetFeedBoostMultiplier() : 1f); FeedEffectPlan feedEffectPlan = BuildFeedEffectPlan(context, dailyFood, levelUpFood, hasLevelUpItems, num2, feedMultiplier, capSingleLevel: true); if (!feedEffectPlan.HasAnyEffect) { feedResponseMessage.Message = "That feed would not change anything right now."; return feedResponseMessage; } feedResponseMessage.Success = true; feedResponseMessage.ConsumedAmount = feedEffectPlan.Amount; TriggerConsumeAnimation(context); if (context.Tameable != null && feedEffectPlan.FedDeltaRequested > 0f) { feedResponseMessage.FedDelta = AddFedTime(context, feedEffectPlan.FedDeltaRequested); } feedResponseMessage.TameDelta = ApplyTameDelta(context, feedEffectPlan.TameDeltaRequested); feedResponseMessage.GrowthDelta = ApplyGrowthDelta(context, feedEffectPlan.GrowthDeltaRequested, out var grewThisFeed); if (feedEffectPlan.StarDeltaRequested > 0) { feedResponseMessage.StarDelta = ApplyStarProgress(context, feedEffectPlan.StarDeltaRequested, singleLevelOnly: true); } if (!grewThisFeed) { feedResponseMessage.HealPercent = ApplyHealPercent(context, feedEffectPlan.HealPercentRequested); } if (!grewThisFeed) { long ticks2 = ZNet.instance.GetTime().AddSeconds(FeedLikeGrandmaPlugin.FeedCooldownSeconds.Value).Ticks; context.NView.GetZDO().Set(FeedCooldownHash, ticks2); feedResponseMessage.DisplayPosition = context.Character.GetTopPoint(); } return feedResponseMessage; } private static void TriggerConsumeAnimation(FeedTargetContext context) { ZSyncAnimation val = context.BaseAI?.m_animator; if (val != null && val.m_animator != null && val.m_animator.parameters.Any((AnimatorControllerParameter parameter) => (int)parameter.type == 9 && parameter.name.Equals("consume", StringComparison.Ordinal))) { val.SetTrigger("consume"); } } private static FeedEffectPlan BuildFeedEffectPlan(FeedTargetContext context, ResolvedFoodEntry? dailyFood, ResolvedFoodEntry? levelUpFood, bool hasLevelUpItems, int amount, float feedMultiplier, bool capSingleLevel) { amount = Mathf.Max(0, amount); bool flag = context.Character.IsTamed(); ResolvedFoodEntry resolvedFoodEntry = (hasLevelUpItems ? levelUpFood : dailyFood); ResolvedFoodEntry resolvedFoodEntry2 = dailyFood ?? levelUpFood; float num = ((resolvedFoodEntry != null && flag && CanUseTamingTimeRequirements(context) && context.Growup == null && CanGainLevel(context)) ? (resolvedFoodEntry.Value * context.Rule.LevelUpConstant * feedMultiplier) : 0f); if (capSingleLevel && num > 0f) { amount = CapAmountForSingleLevelUp(context, amount, num); } int tameDeltaRequested = ((dailyFood != null && !flag && context.Tameable != null) ? Mathf.FloorToInt(dailyFood.Value * context.Rule.TamingConstant * (float)amount * feedMultiplier) : 0); int growthDeltaRequested = ((dailyFood != null && flag && context.Growup != null) ? Mathf.FloorToInt(dailyFood.Value * context.Rule.GrowthConstant * (float)amount * feedMultiplier) : 0); int starDeltaRequested = ((num > 0f) ? Mathf.FloorToInt(num * (float)amount) : 0); float healPercentRequested = ((resolvedFoodEntry2 != null) ? (resolvedFoodEntry2.Value * context.Rule.HealConstant * (float)amount * feedMultiplier) : 0f); float num2 = ((resolvedFoodEntry2 != null && context.Tameable != null) ? (resolvedFoodEntry2.Value * context.Rule.FedConstant * (float)amount * feedMultiplier) : 0f); return new FeedEffectPlan { Amount = amount, TameDeltaRequested = tameDeltaRequested, GrowthDeltaRequested = growthDeltaRequested, StarDeltaRequested = starDeltaRequested, HealPercentRequested = healPercentRequested, FedDeltaRequested = num2, FedDeltaPossible = GetPotentialFedDelta(context, num2) }; } private static int ApplyTameDelta(FeedTargetContext context, int requestedDelta) { if (requestedDelta <= 0 || context.Tameable == null) { return 0; } EnsureLevelScaledTameTime(context); float remainingTime = context.Tameable.GetRemainingTime(); int num = Mathf.Min(requestedDelta, Mathf.CeilToInt(Mathf.Max(0f, remainingTime))); if (num <= 0) { return 0; } float num2 = Mathf.Max(0f, remainingTime - (float)num); context.NView.GetZDO().Set(ZDOVars.s_tameTimeLeft, num2); if (num2 <= 0f && !context.Character.IsTamed()) { context.Tameable.Tame(); } return num; } private static int ApplyGrowthDelta(FeedTargetContext context, int requestedDelta, out bool grewThisFeed) { grewThisFeed = false; if (requestedDelta <= 0 || context.Growup == null) { return 0; } float growthElapsedSeconds = GetGrowthElapsedSeconds(context.Growup, context.Character, initializeIfMissing: true); float num = Mathf.Max(0f, context.Growup.m_growTime - growthElapsedSeconds); int num2 = Mathf.Min(requestedDelta, Mathf.CeilToInt(num)); if (num2 <= 0) { return 0; } SetGrowthElapsedSeconds(context.Growup, growthElapsedSeconds + (float)num2); if (growthElapsedSeconds + (float)num2 >= context.Growup.m_growTime) { grewThisFeed = true; GrowNow(context.Growup, context.Character); } return num2; } private static float ApplyHealPercent(FeedTargetContext context, float requestedPercent) { if (requestedPercent <= 0f) { return 0f; } float maxHealth = context.Character.GetMaxHealth(); if (maxHealth <= 0f) { return 0f; } float health = context.Character.GetHealth(); float num = Mathf.Min(maxHealth - health, maxHealth * requestedPercent / 100f); if (num <= 0f) { return 0f; } context.Character.Heal(num, false); return num / maxHealth * 100f; } private static void ReceiveFeedResponse(Player player, long sender, ZPackage pkg) { //IL_00ab: 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_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_0116: Unknown result type (might be due to invalid IL or missing references) //IL_012d: Unknown result type (might be due to invalid IL or missing references) //IL_016f: Unknown result type (might be due to invalid IL or missing references) //IL_0174: Unknown result type (might be due to invalid IL or missing references) //IL_0180: Unknown result type (might be due to invalid IL or missing references) //IL_0181: Unknown result type (might be due to invalid IL or missing references) //IL_0192: Unknown result type (might be due to invalid IL or missing references) //IL_0197: Unknown result type (might be due to invalid IL or missing references) //IL_01c0: Unknown result type (might be due to invalid IL or missing references) //IL_01c1: Unknown result type (might be due to invalid IL or missing references) //IL_01d2: Unknown result type (might be due to invalid IL or missing references) //IL_01d7: Unknown result type (might be due to invalid IL or missing references) //IL_0204: Unknown result type (might be due to invalid IL or missing references) //IL_0205: Unknown result type (might be due to invalid IL or missing references) //IL_0216: Unknown result type (might be due to invalid IL or missing references) //IL_021b: Unknown result type (might be due to invalid IL or missing references) //IL_024d: Unknown result type (might be due to invalid IL or missing references) //IL_024e: Unknown result type (might be due to invalid IL or missing references) //IL_025f: Unknown result type (might be due to invalid IL or missing references) //IL_0264: Unknown result type (might be due to invalid IL or missing references) //IL_0291: Unknown result type (might be due to invalid IL or missing references) //IL_0292: Unknown result type (might be due to invalid IL or missing references) //IL_02a3: Unknown result type (might be due to invalid IL or missing references) //IL_02a8: Unknown result type (might be due to invalid IL or missing references) if (!FeedResponseMessage.TryRead(pkg, out FeedResponseMessage message) || message == null) { FeedLikeGrandmaPlugin.Log.LogDebug((object)$"Ignored malformed feed response from peer {sender}."); } else { if (!PendingRequests.TryGetValue(message.RequestId, out PendingFeedRequest value)) { return; } if (value.ExpectedResponderId != 0L && value.ExpectedResponderId != sender) { FeedLikeGrandmaPlugin.Log.LogWarning((object)$"Ignored feed response {message.RequestId} from unexpected peer {sender}."); return; } PendingRequests.Remove(message.RequestId); if (!message.Success) { if (!string.IsNullOrWhiteSpace(message.Message)) { ((Character)player).Message((MessageType)2, message.Message, 0, (Sprite)null); } return; } ZDOID targetId = message.TargetId; if (!((ZDOID)(ref targetId)).Equals(value.TargetId) || !message.ItemPrefabName.Equals(value.ItemPrefabName, StringComparison.OrdinalIgnoreCase) || message.ConsumedAmount <= 0 || message.ConsumedAmount > value.RequestedAmount) { FeedLikeGrandmaPlugin.Log.LogWarning((object)$"Rejected mismatched feed response {message.RequestId}: expected {value.ItemPrefabName} for {value.TargetId}, received {message.ItemPrefabName} for {message.TargetId} with amount {message.ConsumedAmount}."); return; } RemoveItemsByPrefab(((Humanoid)player).GetInventory(), value.ItemPrefabName, value.ItemQuality, message.ConsumedAmount); Vector3 displayPosition = message.DisplayPosition; int num = 0; if (message.TameDelta > 0) { ShowFloatingText(displayPosition + Vector3.up * (0.35f * (float)num++), $"+{message.TameDelta}s", (TextType)7); } if (message.GrowthDelta > 0) { ShowFloatingText(displayPosition + Vector3.up * (0.35f * (float)num++), $"+{message.GrowthDelta}s", (TextType)7); } if (message.FedDelta > 0f) { ShowFloatingText(displayPosition + Vector3.up * (0.35f * (float)num++), FeedLocalization.Format("$flg_floating_fed", FormatDurationClock(message.FedDelta)), (TextType)7); } if (message.StarDelta > 0) { ShowFloatingText(displayPosition + Vector3.up * (0.35f * (float)num++), $"+{message.StarDelta}", (TextType)7); } if (message.HealPercent > 0f) { ShowFloatingText(displayPosition + Vector3.up * (0.35f * (float)num++), FeedLocalization.Format("$flg_floating_hp", message.HealPercent.ToString("0.#", CultureInfo.InvariantCulture)), (TextType)4); } if (!string.IsNullOrWhiteSpace(message.Message)) { ((Character)player).Message((MessageType)2, message.Message, 0, (Sprite)null); } } } private static void ApplyAutomaticFeedEffects(FeedTargetContext context, ResolvedFoodEntry? dailyFood, ResolvedFoodEntry? levelUpFood, bool hasLevelUpItems) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) float feedMultiplier = (AnyNearbyPlayerHasTamingBoost(((Component)context.Character).transform.position) ? FeedLikeGrandmaPlugin.GetFeedBoostMultiplier() : 1f); FeedEffectPlan feedEffectPlan = BuildFeedEffectPlan(context, dailyFood, levelUpFood, hasLevelUpItems, 1, feedMultiplier, capSingleLevel: true); if (context.Tameable != null && feedEffectPlan.FedDeltaRequested > 0f) { AddFedTime(context, feedEffectPlan.FedDeltaRequested); } ApplyTameDelta(context, feedEffectPlan.TameDeltaRequested); ApplyGrowthDelta(context, feedEffectPlan.GrowthDeltaRequested, out var grewThisFeed); if (feedEffectPlan.StarDeltaRequested > 0) { ApplyStarProgress(context, feedEffectPlan.StarDeltaRequested, singleLevelOnly: true); } if (!grewThisFeed) { ApplyHealPercent(context, feedEffectPlan.HealPercentRequested); } } private static bool TryGetFeedEntriesForItem(FeedTargetContext context, string itemPrefabName, int itemQuality, out ResolvedFoodEntry? dailyFood, out ResolvedFoodEntry? levelUpFood, out bool hasLevelUpItems) { int itemQuality2 = Mathf.Max(1, itemQuality); dailyFood = FindMatchingFood(context.Rule.Foods, itemPrefabName, itemQuality2); hasLevelUpItems = TryGetLevelUpItems(context, out IReadOnlyList foods); levelUpFood = ((hasLevelUpItems && foods != null) ? FindMatchingFood(foods, itemPrefabName, itemQuality2) : null); if (dailyFood == null) { return levelUpFood != null; } return true; } private static bool TryGetAutoConsumeMatch(FeedTargetContext context, ItemData item, out bool canConsume, out bool matchesLevelUpFood) { canConsume = false; matchesLevelUpFood = false; if (!TryGetItemPrefabName(item, out string prefabName)) { return false; } int itemQuality = Mathf.Max(1, item.m_quality); if (!TryGetFeedEntriesForItem(context, prefabName, itemQuality, out ResolvedFoodEntry dailyFood, out ResolvedFoodEntry levelUpFood, out bool _)) { return true; } canConsume = dailyFood != null || levelUpFood != null; matchesLevelUpFood = levelUpFood != null; return true; } private static void ShowFloatingText(Vector3 position, string text, TextType type) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) if (DamageText.instance != null) { DamageText.instance.ShowText(type, position, text, false); } } private static bool TryGetHoverFoods(Player player, out HoverFoodRows rows) { rows = default(HoverFoodRows); if (player == null) { return false; } if (FeedLikeGrandmaPlugin.RequireCrouchForFoodIcons.Value && !((Character)player).IsCrouching()) { return false; } GameObject hoverObject = ((Humanoid)player).GetHoverObject(); if (hoverObject == null || !TryBuildContext(hoverObject, out var context)) { return false; } if (context.BaseAI != null && context.BaseAI.IsAlerted()) { return false; } IReadOnlyList readOnlyList2; if (!TryGetLevelUpItems(context, out IReadOnlyList foods) || foods == null) { IReadOnlyList readOnlyList = Array.Empty(); readOnlyList2 = readOnlyList; } else { readOnlyList2 = foods; } IReadOnlyList readOnlyList3 = readOnlyList2; IReadOnlyList foods2 = context.Rule.Foods; if (readOnlyList3.Count == 0 && foods2.Count == 0) { return false; } int currentLevel = ((readOnlyList3.Count > 0) ? Mathf.Max(1, context.Character.GetLevel()) : 0); rows = new HoverFoodRows(readOnlyList3, foods2, currentLevel); return true; } private static bool TryGetLevelUpItems(FeedTargetContext context, out IReadOnlyList? foods) { foods = null; if (!ShouldUseLevelUpItems(context)) { return false; } int key = Mathf.Max(1, context.Character.GetLevel()); if (!context.Rule.LevelUpItems.TryGetValue(key, out List value)) { return false; } foods = value; return true; } private static bool ShouldUseLevelUpItems(FeedTargetContext context) { if (context.Character.IsTamed() && context.Tameable != null && context.Growup == null && CanUseTamingTimeRequirements(context)) { return CanGainLevel(context); } return false; } private static ResolvedFoodEntry? FindMatchingFood(IEnumerable foods, string itemPrefabName, int itemQuality) { ResolvedFoodEntry resolvedFoodEntry = null; foreach (ResolvedFoodEntry food in foods) { if (!food.PrefabName.Equals(itemPrefabName, StringComparison.OrdinalIgnoreCase)) { continue; } if (food.RequiredQuality.HasValue) { if (food.RequiredQuality.Value == itemQuality) { return food; } } else if (resolvedFoodEntry == null) { resolvedFoodEntry = food; } } return resolvedFoodEntry; } private static bool TryBuildContext(GameObject hoverObject, out FeedTargetContext context) { return TryBuildContext(hoverObject.GetComponentInParent(), out context); } private static bool TryBuildContext(Character? character, out FeedTargetContext context) { //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Invalid comparison between Unknown and I4 //IL_0037: Unknown result type (might be due to invalid IL or missing references) context = default(FeedTargetContext); if (character == null) { return false; } GameObject gameObject = ((Component)character).gameObject; string text = NormalizePrefabName(((Object)gameObject).name); if (ExcludedPrefabNames.Contains(text) || (int)character.m_faction == 11 || (int)character.m_faction == 0) { return false; } if (!ResolvedRules.TryGetValue(text, out ResolvedFeedingRule value)) { return false; } ZNetView component = gameObject.GetComponent(); if (component == null || !component.IsValid()) { return false; } context = new FeedTargetContext(character, gameObject.GetComponent(), gameObject.GetComponent(), character.GetBaseAI(), component, value); return true; } private static string NormalizePrefabName(string name) { int num = name.IndexOf('('); int num2 = name.IndexOf(' '); int num3 = ((num < 0) ? num2 : ((num2 < 0) ? num : Mathf.Min(num, num2))); if (num3 < 0) { return name; } return name.Substring(0, num3); } private static bool TryGetItemPrefabName(ItemData item, out string prefabName) { prefabName = string.Empty; if (item.m_dropPrefab != null) { prefabName = ((Object)item.m_dropPrefab).name; return true; } GameObject val = default(GameObject); if (ObjectDB.instance != null && ObjectDB.instance.TryGetItemPrefab(item.m_shared, ref val)) { prefabName = ((Object)val).name; return true; } return false; } private static HashSet CollectValidFoodNames(GameObject prefab, HashSet visited) { HashSet hashSet = new HashSet(StringComparer.OrdinalIgnoreCase); if (prefab == null || !visited.Add(((Object)prefab).name)) { return hashSet; } MonsterAI component = prefab.GetComponent(); Tameable component2 = prefab.GetComponent(); if (component != null && component2 != null && component.m_consumeItems != null) { foreach (ItemDrop consumeItem in component.m_consumeItems) { if (consumeItem != null) { hashSet.Add(((Object)((Component)consumeItem).gameObject).name); } } } foreach (GameObject item in EnumerateGrowthTargets(prefab.GetComponent())) { hashSet.UnionWith(CollectValidFoodNames(item, visited)); } return hashSet; } private static IEnumerable EnumerateGrowthTargets(Growup? growup) { if (growup == null) { yield break; } if (growup.m_grownPrefab != null) { yield return growup.m_grownPrefab; } if (growup.m_altGrownPrefabs == null) { yield break; } foreach (GrownEntry altGrownPrefab in growup.m_altGrownPrefabs) { if (altGrownPrefab?.m_prefab != null) { yield return altGrownPrefab.m_prefab; } } } private static float AddFedTime(FeedTargetContext context, float requestedSeconds) { //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) if (context.Tameable == null || requestedSeconds <= 0f) { return 0f; } ApplyEffectiveFedDuration(context); bool flag = context.Tameable.IsHungry(); float maxFedSeconds; float fedRemainingSeconds = GetFedRemainingSeconds(context, out maxFedSeconds); if (maxFedSeconds <= 0f) { return 0f; } float num = Mathf.Clamp(fedRemainingSeconds + requestedSeconds, 0f, maxFedSeconds); SetFedRemainingSeconds(context, num, maxFedSeconds); float num2 = Mathf.Max(0f, num - fedRemainingSeconds); if (num2 > 0f && flag) { context.Tameable.m_sootheEffect.Create(context.Character.GetCenterPoint(), Quaternion.identity, (Transform)null, 1f, -1); } return num2; } private static float GetPotentialFedDelta(FeedTargetContext context, float requestedSeconds) { if (context.Tameable == null || requestedSeconds <= 0f) { return 0f; } float maxFedSeconds; float fedRemainingSeconds = GetFedRemainingSeconds(context, out maxFedSeconds); if (!(maxFedSeconds <= 0f)) { return Mathf.Max(0f, Mathf.Clamp(fedRemainingSeconds + requestedSeconds, 0f, maxFedSeconds) - fedRemainingSeconds); } return 0f; } private static void ApplyEffectiveFedDuration(FeedTargetContext context) { if (context.Tameable != null) { float maxFedSeconds = GetMaxFedSeconds(context); if (maxFedSeconds > 0f) { context.Tameable.m_fedDuration = maxFedSeconds; } } } private static float GetFedRemainingSeconds(FeedTargetContext context, out float maxFedSeconds) { maxFedSeconds = GetMaxFedSeconds(context); if (maxFedSeconds <= 0f || context.Tameable == null || !context.NView.IsValid()) { return 0f; } long num = context.NView.GetZDO().GetLong(ZDOVars.s_tameLastFeeding, 0L); if (num <= 0) { return 0f; } double totalSeconds = (ZNet.instance.GetTime() - new DateTime(num)).TotalSeconds; return Mathf.Clamp(maxFedSeconds - (float)totalSeconds, 0f, maxFedSeconds); } private static void SetFedRemainingSeconds(FeedTargetContext context, float remainingSeconds, float maxFedSeconds) { if (context.NView.IsValid()) { if (remainingSeconds <= 0f || maxFedSeconds <= 0f) { context.NView.GetZDO().Set(ZDOVars.s_tameLastFeeding, 0L); return; } DateTime dateTime = ZNet.instance.GetTime().AddSeconds(0f - (maxFedSeconds - remainingSeconds)); context.NView.GetZDO().Set(ZDOVars.s_tameLastFeeding, dateTime.Ticks); } } private static float GetMaxFedSeconds(FeedTargetContext context) { float baseFedSeconds = GetBaseFedSeconds(context); if (baseFedSeconds <= 0f) { return 0f; } int num = Mathf.Max(1, context.Character.GetLevel()); float num2 = Mathf.Max(1f, FeedLikeGrandmaPlugin.LevelFedDurationMultiplier.Value); float num3 = 1f + (float)(num - 1) * (num2 - 1f); return baseFedSeconds * num3; } private static float GetBaseFedSeconds(FeedTargetContext context) { Tameable component = context.Rule.Prefab.GetComponent(); if (component != null && component.m_fedDuration > 0f) { return component.m_fedDuration; } return context.Tameable?.m_fedDuration ?? 0f; } private static string FormatDurationClock(float seconds) { int num = Mathf.Max(0, Mathf.CeilToInt(seconds)); int num2 = num / 3600; int num3 = num % 3600 / 60; int num4 = num % 60; if (num2 <= 0) { return num3.ToString(CultureInfo.InvariantCulture) + ":" + num4.ToString("00", CultureInfo.InvariantCulture); } return num2.ToString(CultureInfo.InvariantCulture) + ":" + num3.ToString("00", CultureInfo.InvariantCulture) + ":" + num4.ToString("00", CultureInfo.InvariantCulture); } private static int CapAmountForSingleLevelUp(FeedTargetContext context, int amount, float starDeltaPerItem) { if (amount <= 1 || starDeltaPerItem <= 0f) { return amount; } int num = Mathf.Max(1, context.Character.GetLevel()); if (num >= GetMaxAllowedLevel(context)) { return amount; } float requiredStarProgress = GetRequiredStarProgress(context, num + 1); float num2 = Mathf.Clamp(GetStarProgress(context.NView), 0f, requiredStarProgress); float num3 = Mathf.Max(0f, requiredStarProgress - num2); if (num3 <= 0.0001f) { return 1; } return Mathf.Clamp(Mathf.CeilToInt(num3 / starDeltaPerItem), 1, amount); } private static int ApplyStarProgress(FeedTargetContext context, int requestedDelta, bool singleLevelOnly) { if (requestedDelta <= 0) { return 0; } int maxAllowedLevel = GetMaxAllowedLevel(context); int num = Mathf.Max(1, context.Character.GetLevel()); if (num >= maxAllowedLevel) { SetStarProgress(context.NView, 0f); return 0; } float num2 = GetStarProgress(context.NView); int num3 = 0; int num4 = requestedDelta; while (num4 > 0 && num < maxAllowedLevel) { int num5 = num + 1; float requiredStarProgress = GetRequiredStarProgress(context, num5); if (num2 + 0.0001f < requiredStarProgress) { float num6 = requiredStarProgress - num2; int num7 = Mathf.Min(num4, Mathf.CeilToInt(num6)); num2 += (float)num7; num4 -= num7; num3 += num7; if (num2 + 0.0001f < requiredStarProgress) { break; } } num2 -= requiredStarProgress; int level = context.Character.GetLevel(); context.Character.SetLevel(num5); if (context.Character.IsTamed() && context.Character.GetLevel() > level) { SpawnTamedProgressionEffect(((Component)context.Character).transform); } num = num5; ApplyEffectiveFedDuration(context); if (singleLevelOnly) { num2 = 0f; break; } if (num >= maxAllowedLevel) { num2 = 0f; break; } } SetStarProgress(context.NView, num2); return num3; } private static float GetRequiredStarProgress(FeedTargetContext context, int targetLevel) { return GetLevelScaledTamingTime(context.Tameable.m_tamingTime, targetLevel); } private static bool CanUseTamingTimeRequirements(FeedTargetContext context) { if (context.Tameable != null) { return context.Tameable.m_tamingTime > 0f; } return false; } private static void EnsureLevelScaledTameTime(FeedTargetContext context) { if (CanUseTamingTimeRequirements(context) && !context.Character.IsTamed() && context.NView.IsValid() && context.NView.IsOwner()) { ZDO zDO = context.NView.GetZDO(); int num = Mathf.Max(1, context.Character.GetLevel()); float tamingTime = context.Tameable.m_tamingTime; float levelTamingTimeMultiplier = GetLevelTamingTimeMultiplier(); float levelScaledTamingTime = GetLevelScaledTamingTime(tamingTime, num); float num2 = zDO.GetFloat(LevelScaledTameTotalHash, 0f); float left = zDO.GetFloat(LevelScaledTameBaseHash, 0f); float left2 = zDO.GetFloat(LevelScaledTameFactorHash, 0f); if (zDO.GetInt(LevelScaledTameLevelHash, 0) != num || !Approximately(left, tamingTime) || !Approximately(left2, levelTamingTimeMultiplier) || !Approximately(num2, levelScaledTamingTime)) { float remainingTime = context.Tameable.GetRemainingTime(); float num3 = ResolvePreviousTameTotal(tamingTime, remainingTime, num2); float num4 = Mathf.Clamp01(1f - remainingTime / Mathf.Max(1f, num3)); float num5 = levelScaledTamingTime * (1f - num4); zDO.Set(ZDOVars.s_tameTimeLeft, Mathf.Max(0f, num5)); zDO.Set(LevelScaledTameTotalHash, levelScaledTamingTime); zDO.Set(LevelScaledTameBaseHash, tamingTime); zDO.Set(LevelScaledTameFactorHash, levelTamingTimeMultiplier); zDO.Set(LevelScaledTameLevelHash, num, false); } } } private static void GetTameProgress(FeedTargetContext context, out float elapsed, out float total) { elapsed = 0f; total = 0f; if (CanUseTamingTimeRequirements(context)) { int targetLevel = Mathf.Max(1, context.Character.GetLevel()); float tamingTime = context.Tameable.m_tamingTime; float remainingTime = context.Tameable.GetRemainingTime(); float appliedTotal = context.NView.GetZDO().GetFloat(LevelScaledTameTotalHash, 0f); float num = ResolvePreviousTameTotal(tamingTime, remainingTime, appliedTotal); float num2 = Mathf.Clamp01(1f - remainingTime / Mathf.Max(1f, num)); total = GetLevelScaledTamingTime(tamingTime, targetLevel); elapsed = total * num2; } } private static float GetLevelScaledTamingTime(float baseTamingTime, int targetLevel) { if (baseTamingTime <= 0f) { return 0f; } float levelTamingTimeMultiplier = GetLevelTamingTimeMultiplier(); int num = Mathf.Max(0, targetLevel - 1); return baseTamingTime * (1f + (levelTamingTimeMultiplier - 1f) * (float)num); } private static float GetLevelTamingTimeMultiplier() { return Mathf.Max(1f, FeedLikeGrandmaPlugin.LevelTamingTimeMultiplier.Value); } private static float ResolvePreviousTameTotal(float baseTamingTime, float remaining, float appliedTotal) { if (appliedTotal > 0f) { return appliedTotal; } if (!(remaining > baseTamingTime)) { return baseTamingTime; } return remaining; } private static bool Approximately(float left, float right) { return Mathf.Abs(left - right) < 0.001f; } private static float GetStarProgress(ZNetView nview) { return nview.GetZDO().GetFloat(StarProgressHash, 0f); } private static void SetStarProgress(ZNetView nview, float value) { nview.GetZDO().Set(StarProgressHash, Mathf.Max(0f, value)); } private static float AdvanceGrowth(Growup growup, Character character) { //IL_0092: Unknown result type (might be due to invalid IL or missing references) float growthElapsedSeconds = GetGrowthElapsedSeconds(growup, character, initializeIfMissing: true); long ticks = ZNet.instance.GetTime().Ticks; long num = growup.m_nview.GetZDO().GetLong(GrowthLastEvalHash, ticks); if (num == 0L) { growup.m_nview.GetZDO().Set(GrowthLastEvalHash, ticks); return growthElapsedSeconds; } float num2 = (float)new TimeSpan(ticks - num).TotalSeconds; if (num2 <= 0f) { return growthElapsedSeconds; } growup.m_nview.GetZDO().Set(GrowthLastEvalHash, ticks); if (!character.IsTamed()) { return growthElapsedSeconds; } float num3 = (AnyNearbyPlayerHasTamingBoost(((Component)growup).transform.position) ? FeedLikeGrandmaPlugin.GetGrowthBoostMultiplier() : 1f); growthElapsedSeconds += num2 * num3; SetGrowthElapsedSeconds(growup, growthElapsedSeconds); return growthElapsedSeconds; } private static float GetGrowthElapsedSeconds(Growup growup, Character character, bool initializeIfMissing) { ZDO zDO = growup.m_nview.GetZDO(); if (zDO == null) { return 0f; } float num = zDO.GetFloat(GrowthElapsedHash, float.MinValue); if (num != float.MinValue) { return num; } if (!initializeIfMissing) { BaseAI baseAI = character.GetBaseAI(); if (baseAI != null && character.IsTamed()) { return Mathf.Min((float)baseAI.GetTimeSinceSpawned().TotalSeconds, growup.m_growTime); } return 0f; } float num2 = 0f; BaseAI baseAI2 = character.GetBaseAI(); if (baseAI2 != null && character.IsTamed()) { num2 = Mathf.Min((float)baseAI2.GetTimeSinceSpawned().TotalSeconds, growup.m_growTime); } zDO.Set(GrowthElapsedHash, num2); zDO.Set(GrowthLastEvalHash, ZNet.instance.GetTime().Ticks); return num2; } private static void SetGrowthElapsedSeconds(Growup growup, float value) { growup.m_nview.GetZDO().Set(GrowthElapsedHash, Mathf.Max(0f, value)); growup.m_nview.GetZDO().Set(GrowthLastEvalHash, ZNet.instance.GetTime().Ticks); } private static void GrowNow(Growup growup, Character currentCharacter) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) Character component = Object.Instantiate(growup.GetPrefab(), ((Component)growup).transform.position, ((Component)growup).transform.rotation).GetComponent(); if (component != null) { if (growup.m_inheritTame) { component.SetTamed(currentCharacter.IsTamed()); } component.SetLevel(currentCharacter.GetLevel()); if (currentCharacter.IsTamed()) { SpawnTamedProgressionEffect(((Component)component).transform); } } growup.m_nview.Destroy(); } private static void SpawnTamedProgressionEffect(Transform target) { //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) ZNetScene instance = ZNetScene.instance; if (instance != null) { GameObject prefab = instance.GetPrefab("fx_creature_tamed"); if (prefab != null) { Object.Instantiate(prefab, target.position, target.rotation); } } } private static bool AnyNearbyPlayerHasTamingBoost(Vector3 position) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) NearbyTamingBoostPlayers.Clear(); try { Player.GetPlayersInRange(position, 60f, NearbyTamingBoostPlayers); foreach (Player nearbyTamingBoostPlayer in NearbyTamingBoostPlayers) { if (nearbyTamingBoostPlayer != null && ((Character)nearbyTamingBoostPlayer).GetSEMan().HaveStatusAttribute((StatusAttribute)8)) { return true; } } return false; } finally { NearbyTamingBoostPlayers.Clear(); } } private static Player? FindPlayer(ZDOID playerId) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) GameObject val = ZNetScene.instance.FindInstance(playerId); if (val != null) { return val.GetComponent(); } return null; } private static int CountItemsByPrefab(Inventory inventory, string prefabName, int itemQuality) { int num = 0; foreach (ItemData allItem in inventory.GetAllItems()) { if (ItemMatches(allItem, prefabName, itemQuality)) { num += allItem.m_stack; } } return num; } private static void RemoveItemsByPrefab(Inventory inventory, string prefabName, int itemQuality, int amount) { if (amount <= 0) { return; } foreach (ItemData item in inventory.GetAllItems().ToList()) { if (ItemMatches(item, prefabName, itemQuality)) { int num = Mathf.Min(amount, item.m_stack); inventory.RemoveItem(item, num); amount -= num; if (amount <= 0) { break; } } } } private static bool ItemMatches(ItemData item, string prefabName, int itemQuality) { if (item != null && Mathf.Max(1, item.m_quality) == Mathf.Max(1, itemQuality) && TryGetItemPrefabName(item, out string prefabName2)) { return prefabName2.Equals(prefabName, StringComparison.OrdinalIgnoreCase); } return false; } } [Flags] internal enum RuntimeRefreshFlags { None = 0, TamingOrb = 1, FeedBarrel = 2, FeedRules = 4, All = 7 } [BepInPlugin("sighsorry.FeedLikeGrandma", "FeedLikeGrandma", "1.0.0")] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] public sealed class FeedLikeGrandmaPlugin : BaseUnityPlugin { public enum Toggle { On = 1, Off = 0 } public enum LevelUpLimit { Vanilla, Level } public enum FeedBarrelMaterial { Ectoplasm, Default } private sealed class ConfigurationManagerAttributes { [UsedImplicitly] public int? Order; [UsedImplicitly] public bool? Browsable; [UsedImplicitly] public string? Category; [UsedImplicitly] public Action? CustomDrawer; } internal const string ModName = "FeedLikeGrandma"; internal const string ModVersion = "1.0.0"; internal const string Author = "sighsorry"; private const string ModGuid = "sighsorry.FeedLikeGrandma"; private static readonly string ConfigFileName = "sighsorry.FeedLikeGrandma.cfg"; private static readonly string ConfigFileFullPath = Path.Combine(Paths.ConfigPath, ConfigFileName); internal static readonly string ConfigDirectoryPath = Path.Combine(Paths.ConfigPath, "FeedLikeGrandma"); internal static readonly string YamlFileFullPath = Path.Combine(ConfigDirectoryPath, "feed.yml"); internal static readonly string ReferenceFileFullPath = Path.Combine(ConfigDirectoryPath, "feed.reference.yml"); private const float FileReloadDebounceSeconds = 0.25f; internal static readonly ManualLogSource Log = Logger.CreateLogSource("FeedLikeGrandma"); internal static readonly ConfigSync ConfigSync = new ConfigSync("sighsorry.FeedLikeGrandma") { DisplayName = "FeedLikeGrandma", CurrentVersion = "1.0.0", MinimumRequiredVersion = "1.0.0" }; internal static FeedLikeGrandmaPlugin Instance = null; internal static CustomSyncedValue SyncedYaml = null; internal static ConfigEntry ServerConfigLocked = null; internal static ConfigEntry MaxItemsPerFeed = null; internal static ConfigEntry FeedCooldownSeconds = null; internal static ConfigEntry LevelUpLimitMode = null; internal static ConfigEntry LevelUpMaxLevel = null; internal static ConfigEntry LevelTamingTimeMultiplier = null; internal static ConfigEntry LevelFedDurationMultiplier = null; internal static ConfigEntry HorizontalProcreationLimit = null; internal static ConfigEntry FeedBarrelRecipe = null; internal static ConfigEntry FeedBarrelMaterialStyle = null; internal static ConfigEntry FeedBarrelContainerColumns = null; internal static ConfigEntry FeedBarrelContainerRows = null; internal static ConfigEntry StableMountCamera = null; internal static ConfigEntry MountStaminaDrainMultiplier = null; internal static ConfigEntry MountStaminaRegenMultiplier = null; internal static ConfigEntry RidingSkillStaminaRegenMultiplierAt100 = null; internal static ConfigEntry EncumberedMountSpeedReduction = null; internal static ConfigEntry EncumberedWalkStaminaDrain = null; internal static ConfigEntry EncumberedMountStaminaDrainMultiplier = null; internal static ConfigEntry EncumberedMountStaminaRegenMultiplier = null; internal static ConfigEntry BlockEncumberedDragonFlight = null; internal static ConfigEntry BetterRidingFeedHealPriority = null; internal static ConfigEntry MonstrumMountJumpCompatibility = null; internal static ConfigEntry DefaultDragonRestrictedBiomes = null; internal static ConfigEntry MeadTamerTameMultiplier = null; internal static ConfigEntry MeadTamerGrowthMultiplier = null; internal static ConfigEntry MeadTamerFeedMultiplier = null; internal static ConfigEntry RequireCrouchForFoodIcons = null; internal static ConfigEntry FoodIconSize = null; internal static ConfigEntry TamingOrbRecipe = null; internal static ConfigEntry TamingOrbCraftingStation = null; private readonly Harmony _harmony = new Harmony("sighsorry.FeedLikeGrandma"); private readonly object _reloadLock = new object(); private FileSystemWatcher? _configWatcher; private FileSystemWatcher? _yamlWatcher; private Coroutine? _configReloadCoroutine; private Coroutine? _yamlReloadCoroutine; private int _configReloadGeneration; private int _yamlReloadGeneration; private RuntimeRefreshFlags _pendingRuntimeRefresh; private Coroutine? _runtimeRefreshCoroutine; private int _runtimeRefreshGeneration; private bool _isShuttingDown; public void Awake() { Instance = this; FeedLocalization.Load(); bool saveOnConfigSet = ((BaseUnityPlugin)this).Config.SaveOnConfigSet; ((BaseUnityPlugin)this).Config.SaveOnConfigSet = false; BindConfigEntries(); SubscribeRuntimeConfigChanges(); EnsureYamlFileExists(); SyncedYaml = new CustomSyncedValue(ConfigSync, "YamlText", string.Empty); SyncedYaml.ValueChanged += OnSyncedYamlChanged; ConfigSync.SourceOfTruthChanged += OnSourceOfTruthChanged; FeedConsoleCommands.Register(); FeedSystem.LoadYamlText(File.ReadAllText(YamlFileFullPath), "local fallback"); ConfigSync.AddLockingConfigEntry(ServerConfigLocked); _harmony.PatchAll(Assembly.GetExecutingAssembly()); if (ConfigSync.IsSourceOfTruth) { PushLocalYamlToSync(); } ((BaseUnityPlugin)this).Config.Save(); ((BaseUnityPlugin)this).Config.SaveOnConfigSet = saveOnConfigSet; SetupWatchers(); } private void OnDestroy() { _isShuttingDown = true; UnsubscribeRuntimeConfigChanges(); if (_runtimeRefreshCoroutine != null) { ((MonoBehaviour)this).StopCoroutine(_runtimeRefreshCoroutine); _runtimeRefreshCoroutine = null; } if (_configReloadCoroutine != null) { ((MonoBehaviour)this).StopCoroutine(_configReloadCoroutine); _configReloadCoroutine = null; } if (_yamlReloadCoroutine != null) { ((MonoBehaviour)this).StopCoroutine(_yamlReloadCoroutine); _yamlReloadCoroutine = null; } _pendingRuntimeRefresh = RuntimeRefreshFlags.None; SyncedYaml.ValueChanged -= OnSyncedYamlChanged; ConfigSync.SourceOfTruthChanged -= OnSourceOfTruthChanged; TamingOrbSystem.Shutdown(); FeedBetterRidingCompatibility.Shutdown(); FeedDragonFlightRestrictionSystem.Shutdown(); FeedRidingSystem.Shutdown(); FeedAutoFeedSystem.Shutdown(); FeedSystem.Shutdown(); FeedReferenceWriter.Shutdown(); FeedExternalReferenceSupport.Shutdown(); _yamlWatcher?.Dispose(); _configWatcher?.Dispose(); _harmony.UnpatchSelf(); } private void BindConfigEntries() { //IL_010d: Unknown result type (might be due to invalid IL or missing references) //IL_0122: Expected O, but got Unknown //IL_0145: Unknown result type (might be due to invalid IL or missing references) //IL_015a: Expected O, but got Unknown //IL_01ae: Unknown result type (might be due to invalid IL or missing references) //IL_01c3: Expected O, but got Unknown //IL_01e7: Unknown result type (might be due to invalid IL or missing references) //IL_01fc: Expected O, but got Unknown //IL_022a: Unknown result type (might be due to invalid IL or missing references) //IL_023f: Expected O, but got Unknown //IL_0288: Unknown result type (might be due to invalid IL or missing references) //IL_029d: Expected O, but got Unknown //IL_02cb: Unknown result type (might be due to invalid IL or missing references) //IL_02e0: Expected O, but got Unknown //IL_030e: Unknown result type (might be due to invalid IL or missing references) //IL_0323: Expected O, but got Unknown //IL_0351: Unknown result type (might be due to invalid IL or missing references) //IL_0366: Expected O, but got Unknown //IL_0394: Unknown result type (might be due to invalid IL or missing references) //IL_03a9: Expected O, but got Unknown //IL_03d7: Unknown result type (might be due to invalid IL or missing references) //IL_03ec: Expected O, but got Unknown //IL_0440: Unknown result type (might be due to invalid IL or missing references) //IL_0455: Expected O, but got Unknown //IL_0483: Unknown result type (might be due to invalid IL or missing references) //IL_0498: Expected O, but got Unknown //IL_04c6: Unknown result type (might be due to invalid IL or missing references) //IL_04db: Expected O, but got Unknown //IL_0555: Unknown result type (might be due to invalid IL or missing references) //IL_056a: Expected O, but got Unknown //IL_0598: Unknown result type (might be due to invalid IL or missing references) //IL_05ad: Expected O, but got Unknown ServerConfigLocked = config("01 - General", "Lock Configuration", Toggle.On, "If on, the synced config and YAML are controlled by the server.", synchronizedSetting: true, 1000); HorizontalProcreationLimit = config("01 - General", "Horizontal Procreation Limit", Toggle.On, "If on, breeding total limits count adults and offspring by horizontal XZ distance, preventing vertical breeder limit bypasses.", synchronizedSetting: true, 990); TamingOrbRecipe = config("01 - General", "Taming Orb Recipe", "AncientSeed:3, Ectoplasm:5, SurtlingCore:7, Chitin:9", "Crafting recipe for FLG_TamingOrb. Format: ItemPrefab:Amount,OtherPrefab:Amount. Leave empty to disable Taming Orb.", synchronizedSetting: true, 980); TamingOrbCraftingStation = config("01 - General", "Taming Orb Crafting Station", "piece_workbench:1", "Crafting station for FLG_TamingOrb. Format: StationPrefab:Level. Use None for no station.", synchronizedSetting: true, 970); FeedBarrelRecipe = config("01 - General", "Feed Barrel Recipe", "BarrelRings:1, Ectoplasm:3, RoundLog:6, ElderBark:9", "Build cost for Feed Barrel. Format: ItemPrefab:Amount,OtherPrefab:Amount. Leave empty to disable the Feed Barrel and container auto-feed.", synchronizedSetting: true, 960); FeedBarrelMaterialStyle = config("01 - General", "Feed Barrel Material", FeedBarrelMaterial.Ectoplasm, "Visual material for Feed Barrel. Ectoplasm applies Valheim's ectoplasm_mat and regenerates the hammer piece icon; Default keeps the source barrel material.", synchronizedSetting: true, 950); FeedBarrelContainerColumns = config("01 - General", "Feed Barrel Container Columns", 6, new ConfigDescription("Inventory width for Feed Barrel. Clamped to 6-8 columns.", (AcceptableValueBase)(object)new AcceptableValueRange(6, 8), Array.Empty()), synchronizedSetting: true, 940); FeedBarrelContainerRows = config("01 - General", "Feed Barrel Container Rows", 2, new ConfigDescription("Inventory height for Feed Barrel. Clamped to 2-100 rows.", (AcceptableValueBase)(object)new AcceptableValueRange(2, 100), Array.Empty()), synchronizedSetting: true, 939); RequireCrouchForFoodIcons = config("02 - Feeding", "Require Crouch For Food Icons", value: true, "If on, food icons are only shown while the local player is crouching.", synchronizedSetting: false, 1000); FoodIconSize = config("02 - Feeding", "Food Icon Size", 36f, new ConfigDescription("Size of each hover food icon in pixels.", (AcceptableValueBase)(object)new AcceptableValueRange(16f, 96f), Array.Empty()), synchronizedSetting: false, 990); MaxItemsPerFeed = config("02 - Feeding", "Max Items Per Feed", 10, new ConfigDescription("Maximum number of items consumed by one manual feed action.", (AcceptableValueBase)(object)new AcceptableValueRange(1, 100), Array.Empty()), synchronizedSetting: true, 980); FeedCooldownSeconds = config("02 - Feeding", "Feed Cooldown Seconds", 3f, new ConfigDescription("Cooldown between manual feed actions on the same creature.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 300f), Array.Empty()), synchronizedSetting: true, 970); LevelUpLimitMode = config("03 - Leveling", "Level Up Limit Mode", LevelUpLimit.Vanilla, "Vanilla keeps Lox and Hen at Character level 1 and lets other creatures reach Character level 3. Level uses the Level Up Max Level setting.", synchronizedSetting: true, 1000); LevelUpMaxLevel = config("03 - Leveling", "Level Up Max Level", 3, new ConfigDescription("Maximum Character level FLG can raise creatures to when Level Up Limit Mode is Level.", (AcceptableValueBase)(object)new AcceptableValueRange(1, 100), Array.Empty()), synchronizedSetting: true, 990); LevelTamingTimeMultiplier = config("03 - Leveling", "Level Taming Time Multiplier", 1.5f, new ConfigDescription("Scales tame.tamingTime by target creature level. Level 1 uses tamingTime; each higher target level adds tamingTime * (value - 1).", (AcceptableValueBase)(object)new AcceptableValueRange(1f, 10f), Array.Empty()), synchronizedSetting: true, 980); LevelFedDurationMultiplier = config("03 - Leveling", "Level Fed Duration Multiplier", 1.5f, new ConfigDescription("Scales tame.fedDuration by creature level for FLG's stacked fed gauge. Level 1 uses fedDuration; each higher level adds fedDuration * (value - 1).", (AcceptableValueBase)(object)new AcceptableValueRange(1f, 10f), Array.Empty()), synchronizedSetting: true, 970); MeadTamerTameMultiplier = config("04 - MeadTamer", "Passive Tame Multiplier", 2f, new ConfigDescription("Multiplier applied to passive taming speed while a nearby player has the TamingBoost status attribute. x2.0 means double speed.", (AcceptableValueBase)(object)new AcceptableValueRange(1f, 6f), Array.Empty()), synchronizedSetting: true, 1000); MeadTamerGrowthMultiplier = config("04 - MeadTamer", "Passive Growth Multiplier", 1.5f, new ConfigDescription("Multiplier applied to passive growth speed while a nearby player has the TamingBoost status attribute. x1.5 means 50% faster growth.", (AcceptableValueBase)(object)new AcceptableValueRange(1f, 6f), Array.Empty()), synchronizedSetting: true, 990); MeadTamerFeedMultiplier = config("04 - MeadTamer", "Feed Multiplier", 1.5f, new ConfigDescription("Multiplier applied to FLG manual and automatic feeding while the feeding player, or any nearby player for automatic feeding, has the TamingBoost status attribute. Affects taming, growth, level-up, healing, and fed time.", (AcceptableValueBase)(object)new AcceptableValueRange(1f, 6f), Array.Empty()), synchronizedSetting: true, 980); StableMountCamera = config("05 - Riding", "Stable Mount Camera", Toggle.On, "If on, skips Valheim's camera tilt while riding a saddle.", synchronizedSetting: false, 1000); MountStaminaDrainMultiplier = config("05 - Riding", "Mount Stamina Drain Multiplier", 1f, new ConfigDescription("Multiplier applied to saddle stamina costs.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 10f), Array.Empty()), synchronizedSetting: true, 990); MountStaminaRegenMultiplier = config("05 - Riding", "Mount Stamina Regen Multiplier", 3f, new ConfigDescription("Base multiplier applied to saddle stamina regeneration while the local rider is not encumbered.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 10f), Array.Empty()), synchronizedSetting: true, 980); RidingSkillStaminaRegenMultiplierAt100 = config("05 - Riding", "Riding Skill Stamina Regen Multiplier At 100", 5f, new ConfigDescription("Riding-skill multiplier applied after the selected normal or encumbered regen multiplier. Scales linearly from x1 at Riding 0 to this value at Riding 100.", (AcceptableValueBase)(object)new AcceptableValueRange(1f, 10f), Array.Empty()), synchronizedSetting: true, 970); EncumberedMountSpeedReduction = config("05 - Riding", "Encumbered Mount Speed Reduction", Toggle.On, "If on, a mounted creature is treated as encumbered while its local rider is encumbered. Valheim then blocks run/jump and uses the creature's m_crouchSpeed, which is 2f by vanilla default.", synchronizedSetting: true, 960); EncumberedWalkStaminaDrain = config("05 - Riding", "Encumbered Walk Stamina Drain", Toggle.On, "If on, walking on a mount while encumbered drains saddle stamina. Uses vanilla saddle run stamina drain as the base, scaled by riding skill and Encumbered Mount Stamina Drain Multiplier.", synchronizedSetting: true, 950); EncumberedMountStaminaDrainMultiplier = config("05 - Riding", "Encumbered Mount Stamina Drain Multiplier", 2f, new ConfigDescription("Multiplier applied to saddle stamina costs while the local rider is encumbered.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 10f), Array.Empty()), synchronizedSetting: true, 940); EncumberedMountStaminaRegenMultiplier = config("05 - Riding", "Encumbered Mount Stamina Regen Multiplier", 0f, new ConfigDescription("Base multiplier applied to saddle stamina regeneration while the local rider is encumbered. At x0, Riding skill cannot restore regeneration.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 10f), Array.Empty()), synchronizedSetting: true, 930); BlockEncumberedDragonFlight = config("06 - Compatibility", "Block Encumbered Dragon Flight", Toggle.On, "If on, BetterRiding dragons cannot start or continue flying while the local rider is encumbered.", synchronizedSetting: true, 1000); BetterRidingFeedHealPriority = config("06 - Compatibility", "FLG Feed Heal Priority", Toggle.On, "If on, FLG reapplies its automatic feed/heal result after other OnConsumedItem postfixes such as BetterRiding's full heal.", synchronizedSetting: true, 990); MonstrumMountJumpCompatibility = config("06 - Compatibility", "Monstrum Mount Jump Compatibility", Toggle.On, "If on, jump stays on the saddle for Monstrum and MonstrumDeepNorth rideable creatures instead of triggering their forced dismount path.", synchronizedSetting: false, 980); DefaultDragonRestrictedBiomes = config("06 - Compatibility", "Default Dragon Restricted Biomes", "Ashlands", "Comma-separated biome names used for dragons without a prefab restrictedBiome override. Leave empty for no global default; prefab restrictedBiome entries still apply. Expand World Data custom biome display names are supported when available.", synchronizedSetting: true, 960); } private void EnsureYamlFileExists() { Directory.CreateDirectory(ConfigDirectoryPath); if (!File.Exists(YamlFileFullPath)) { File.WriteAllText(YamlFileFullPath, FeedReferenceWriter.BuildDefaultOverrideYaml()); } } internal static void RefreshGeneratedFilesIfNeeded() { if (!IsGameDataReady() || !ConfigSync.IsSourceOfTruth) { return; } try { Directory.CreateDirectory(ConfigDirectoryPath); if (WriteFileIfChanged(ReferenceFileFullPath, FeedReferenceWriter.BuildReferenceYaml(refreshExternalSnapshot: false))) { Log.LogInfo((object)("Updated generated FeedLikeGrandma reference YAML at " + ReferenceFileFullPath + ".")); } } catch (Exception arg) { Log.LogWarning((object)$"Failed to refresh generated FeedLikeGrandma YAML files: {arg}"); } } internal static bool TryWriteReferenceConfigurationFile(out string path, out string error) { path = ReferenceFileFullPath; error = string.Empty; if (!IsGameDataReady()) { error = "FeedLikeGrandma game data is not ready yet."; return false; } Directory.CreateDirectory(ConfigDirectoryPath); File.WriteAllText(path, FeedReferenceWriter.BuildReferenceYaml()); Log.LogInfo((object)("Wrote FeedLikeGrandma reference YAML to " + path + ".")); return true; } private static bool WriteFileIfChanged(string path, string content) { if (File.Exists(path) && string.Equals(File.ReadAllText(path), content, StringComparison.Ordinal)) { return false; } File.WriteAllText(path, content); return true; } private static bool IsGameDataReady() { if ((Object)(object)ZNetScene.instance != (Object)null) { return (Object)(object)ObjectDB.instance != (Object)null; } return false; } private void SetupWatchers() { _configWatcher = new FileSystemWatcher(Paths.ConfigPath, ConfigFileName); _configWatcher.Changed += OnConfigFileChanged; _configWatcher.Created += OnConfigFileChanged; _configWatcher.Renamed += OnConfigFileChanged; _configWatcher.IncludeSubdirectories = false; _configWatcher.SynchronizingObject = ThreadingHelper.SynchronizingObject; _configWatcher.EnableRaisingEvents = true; Directory.CreateDirectory(ConfigDirectoryPath); _yamlWatcher = new FileSystemWatcher(ConfigDirectoryPath, Path.GetFileName(YamlFileFullPath)); _yamlWatcher.Changed += OnYamlFileChanged; _yamlWatcher.Created += OnYamlFileChanged; _yamlWatcher.Renamed += OnYamlFileChanged; _yamlWatcher.IncludeSubdirectories = false; _yamlWatcher.SynchronizingObject = ThreadingHelper.SynchronizingObject; _yamlWatcher.EnableRaisingEvents = true; } private void OnConfigFileChanged(object sender, FileSystemEventArgs e) { if (!_isShuttingDown) { _configReloadGeneration++; if (_configReloadCoroutine == null) { _configReloadCoroutine = ((MonoBehaviour)this).StartCoroutine(ReloadConfigAfterQuietPeriod()); } } } private IEnumerator ReloadConfigAfterQuietPeriod() { while (!_isShuttingDown) { int generation = _configReloadGeneration; yield return (object)new WaitForSecondsRealtime(0.25f); if (generation == _configReloadGeneration) { ReloadConfigFromDisk(); break; } } _configReloadCoroutine = null; } private void ReloadConfigFromDisk() { lock (_reloadLock) { if (!File.Exists(ConfigFileFullPath)) { return; } try { ((BaseUnityPlugin)this).Config.Reload(); QueueRuntimeRefresh(RuntimeRefreshFlags.All); } catch (Exception arg) { Log.LogError((object)$"Failed to reload config: {arg}"); } } } private void OnYamlFileChanged(object sender, FileSystemEventArgs e) { if (!_isShuttingDown) { _yamlReloadGeneration++; if (_yamlReloadCoroutine == null) { _yamlReloadCoroutine = ((MonoBehaviour)this).StartCoroutine(ReloadYamlAfterQuietPeriod()); } } } private IEnumerator ReloadYamlAfterQuietPeriod() { while (!_isShuttingDown) { int generation = _yamlReloadGeneration; yield return (object)new WaitForSecondsRealtime(0.25f); if (generation == _yamlReloadGeneration) { ReloadYamlFromDisk(); break; } } _yamlReloadCoroutine = null; } private void ReloadYamlFromDisk() { lock (_reloadLock) { if (!File.Exists(YamlFileFullPath)) { return; } if (!ConfigSync.IsSourceOfTruth) { Log.LogDebug((object)"Ignoring local YAML change while server data is authoritative."); return; } try { PushLocalYamlToSync(); } catch (Exception arg) { Log.LogError((object)$"Failed to reload YAML: {arg}"); } } } private void PushLocalYamlToSync() { string value = File.ReadAllText(YamlFileFullPath); SyncedYaml.Value = value; } private void OnSyncedYamlChanged() { FeedSystem.LoadYamlText(SyncedYaml.Value ?? string.Empty, ConfigSync.IsSourceOfTruth ? "local sync" : "server sync"); } private void OnSourceOfTruthChanged(bool isSourceOfTruth) { if (isSourceOfTruth) { PushLocalYamlToSync(); } else if (!string.IsNullOrWhiteSpace(SyncedYaml.Value)) { FeedSystem.LoadYamlText(SyncedYaml.Value, "server sync"); } } internal static float GetTamingBoostMultiplier() { return Math.Max(1f, MeadTamerTameMultiplier.Value); } internal static float GetGrowthBoostMultiplier() { return Math.Max(1f, MeadTamerGrowthMultiplier.Value); } internal static float GetFeedBoostMultiplier() { return Math.Max(1f, MeadTamerFeedMultiplier.Value); } internal static void QueueRuntimeRefresh(RuntimeRefreshFlags flags) { if (flags != RuntimeRefreshFlags.None && Instance != null && !Instance._isShuttingDown) { Instance._pendingRuntimeRefresh |= flags; Instance._runtimeRefreshGeneration++; FeedLikeGrandmaPlugin instance = Instance; if (instance._runtimeRefreshCoroutine == null) { instance._runtimeRefreshCoroutine = ((MonoBehaviour)Instance).StartCoroutine(Instance.FlushRuntimeRefresh()); } } } private IEnumerator FlushRuntimeRefresh() { while (!_isShuttingDown) { int generation = _runtimeRefreshGeneration; yield return null; if (generation != _runtimeRefreshGeneration) { continue; } RuntimeRefreshFlags pendingRuntimeRefresh = _pendingRuntimeRefresh; _pendingRuntimeRefresh = RuntimeRefreshFlags.None; if ((pendingRuntimeRefresh & RuntimeRefreshFlags.TamingOrb) != RuntimeRefreshFlags.None) { TryRefreshRuntime("Taming Orb registration", delegate { TamingOrbSystem.RegisterPrefabsAndRecipes(); }); } if ((pendingRuntimeRefresh & RuntimeRefreshFlags.FeedBarrel) != RuntimeRefreshFlags.None) { TryRefreshRuntime("Feed Barrel registration", delegate { FeedBarrelSystem.RegisterPrefabsAndPieces(); }); } if ((pendingRuntimeRefresh & RuntimeRefreshFlags.FeedRules) != RuntimeRefreshFlags.None) { TryRefreshRuntime("feed rule rebuild", FeedSystem.RebuildRuntimeData); } if (_pendingRuntimeRefresh == RuntimeRefreshFlags.None) { break; } } _runtimeRefreshCoroutine = null; } private static void TryRefreshRuntime(string operation, Action refresh) { try { refresh(); } catch (Exception arg) { Log.LogError((object)$"Failed to refresh {operation}: {arg}"); } } private static void OnTamingOrbConfigChanged(object sender, EventArgs e) { QueueRuntimeRefresh(RuntimeRefreshFlags.TamingOrb); } private static void OnFeedBarrelConfigChanged(object sender, EventArgs e) { QueueRuntimeRefresh(RuntimeRefreshFlags.FeedBarrel); } private static void OnDragonFlightConfigChanged(object sender, EventArgs e) { FeedDragonFlightRestrictionSystem.OnRulesChanged(); } private static void SubscribeRuntimeConfigChanges() { TamingOrbRecipe.SettingChanged += OnTamingOrbConfigChanged; TamingOrbCraftingStation.SettingChanged += OnTamingOrbConfigChanged; FeedBarrelRecipe.SettingChanged += OnFeedBarrelConfigChanged; FeedBarrelMaterialStyle.SettingChanged += OnFeedBarrelConfigChanged; FeedBarrelContainerColumns.SettingChanged += OnFeedBarrelConfigChanged; FeedBarrelContainerRows.SettingChanged += OnFeedBarrelConfigChanged; DefaultDragonRestrictedBiomes.SettingChanged += OnDragonFlightConfigChanged; } private static void UnsubscribeRuntimeConfigChanges() { TamingOrbRecipe.SettingChanged -= OnTamingOrbConfigChanged; TamingOrbCraftingStation.SettingChanged -= OnTamingOrbConfigChanged; FeedBarrelRecipe.SettingChanged -= OnFeedBarrelConfigChanged; FeedBarrelMaterialStyle.SettingChanged -= OnFeedBarrelConfigChanged; FeedBarrelContainerColumns.SettingChanged -= OnFeedBarrelConfigChanged; FeedBarrelContainerRows.SettingChanged -= OnFeedBarrelConfigChanged; DefaultDragonRestrictedBiomes.SettingChanged -= OnDragonFlightConfigChanged; } private ConfigEntry config(string group, string name, T value, string description, bool synchronizedSetting = true, int? order = null) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Expected O, but got Unknown return config(group, name, value, new ConfigDescription(description, (AcceptableValueBase)null, Array.Empty()), synchronizedSetting, order); } private ConfigEntry config(string group, string name, T value, ConfigDescription description, bool synchronizedSetting = true, int? order = null) { //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Expected O, but got Unknown object[] array = description.Tags ?? Array.Empty(); if (order.HasValue) { object[] array2 = new object[array.Length + 1]; Array.Copy(array, array2, array.Length); array2[^1] = new ConfigurationManagerAttributes { Order = order.Value }; array = array2; } ConfigDescription val = new ConfigDescription(description.Description + (synchronizedSetting ? " [Synced with Server]" : " [Not Synced with Server]"), description.AcceptableValues, array); ConfigEntry val2 = ((BaseUnityPlugin)this).Config.Bind(group, name, value, val); ConfigSync.AddConfigEntry(val2).SynchronizedConfig = synchronizedSetting; return val2; } } public static class ToggleExtensions { public static bool IsOn(this FeedLikeGrandmaPlugin.Toggle value) { return value == FeedLikeGrandmaPlugin.Toggle.On; } } internal static class TamingOrbSystem { private sealed class SharedDataReferenceComparer : IEqualityComparer { public bool Equals(SharedData? x, SharedData? y) { return x == y; } public int GetHashCode(SharedData obj) { return RuntimeHelpers.GetHashCode(obj); } } private sealed class CapturedTamePayload { public string Prefab { get; init; } = string.Empty; public string Name { get; init; } = string.Empty; public int Level { get; init; } public float Health { get; init; } public float MaxHealth { get; init; } public bool HasSaddle { get; init; } public string Inventory { get; init; } = string.Empty; public long LastFedAgeTicks { get; init; } public long SpawnAgeTicks { get; init; } public float StarProgress { get; init; } public float GrowthElapsed { get; init; } public int CllcEffect { get; init; } public string CllcInfusion { get; init; } = string.Empty; public string CllcExtraEffect { get; init; } = string.Empty; public bool OdinHorseHasArmor { get; init; } public float SaddleStamina { get; init; } public string Serialize() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00b7: 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_00d8: Unknown result type (might be due to invalid IL or missing references) //IL_00ed: Unknown result type (might be due to invalid IL or missing references) ZPackage val = new ZPackage(); val.Write(3); val.Write(Prefab ?? string.Empty); val.Write(Name ?? string.Empty); val.Write(Level); val.Write(Health); val.Write(MaxHealth); val.Write(HasSaddle); val.Write(Inventory ?? string.Empty); val.Write(LastFedAgeTicks); val.Write(SpawnAgeTicks); val.Write(StarProgress); val.Write(GrowthElapsed); val.Write(CllcEffect); val.Write(SaddleStamina); val.Write(CllcInfusion ?? string.Empty); val.Write(CllcExtraEffect ?? string.Empty); val.Write(OdinHorseHasArmor); return Convert.ToBase64String(val.GetArray()); } public static bool TryDeserialize(string text, out CapturedTamePayload? payload, bool logWarnings = true) { //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Expected O, but got Unknown payload = null; try { ZPackage val = new ZPackage(Convert.FromBase64String(text)); if (val.ReadInt() != 3) { return false; } string prefab = val.ReadString(); string name = val.ReadString(); int level = val.ReadInt(); float health = val.ReadSingle(); float maxHealth = val.ReadSingle(); bool hasSaddle = val.ReadBool(); string inventory = val.ReadString(); long lastFedAgeTicks = val.ReadLong(); long spawnAgeTicks = val.ReadLong(); float starProgress = val.ReadSingle(); float growthElapsed = val.ReadSingle(); int cllcEffect = val.ReadInt(); float saddleStamina = val.ReadSingle(); string cllcInfusion = val.ReadString(); string cllcExtraEffect = val.ReadString(); bool odinHorseHasArmor = val.ReadBool(); payload = new CapturedTamePayload { Prefab = prefab, Name = name, Level = level, Health = health, MaxHealth = maxHealth, HasSaddle = hasSaddle, Inventory = inventory, LastFedAgeTicks = lastFedAgeTicks, SpawnAgeTicks = spawnAgeTicks, StarProgress = starProgress, GrowthElapsed = growthElapsed, CllcEffect = cllcEffect, CllcInfusion = cllcInfusion, CllcExtraEffect = cllcExtraEffect, OdinHorseHasArmor = odinHorseHasArmor, SaddleStamina = saddleStamina }; return !string.IsNullOrWhiteSpace(payload.Prefab); } catch (Exception ex) { if (logWarnings) { FeedLikeGrandmaPlugin.Log.LogWarning((object)("Failed to read FLG_TamingOrb payload: " + ex.Message)); } return false; } } } internal const string PrefabName = "FLG_TamingOrb"; private const string RecipeName = "Recipe_FLG_TamingOrb"; private const string PayloadKey = "flg.tame.payload"; private const string PayloadIconPrefabKey = "flg.tame.icon_prefab"; private const int PayloadVersion = 3; private const string DefaultSourcePrefabName = "DragonTear"; private const string CllcEffectKey = "CL&LC effect"; private const string OdinHorseHaveArmorKey = "HaveArmor"; private const string ActionEffectPrefab = "fx_summon_start"; private const float ActionDelay = 4.5f; private const float CaptureRange = 5f; private const float ReleaseAimRange = 5f; private const float ReleaseFallbackAimDistance = 1f; private const float ReleaseUpOffset = 1f; private const int IconRenderLayer = 31; private const int CreatureIconSize = 128; private const float CreatureIconOverlayScale = 0.78f; private const int CreatureIconBorderClearPixels = 2; private const string IconCacheRevision = "raw-v4-composite78-border2"; private const string IconCacheMagic = "FLGICON"; private const int IconCacheFormatVersion = 1; private static readonly Color TamingOrbGreen = new Color(0.18f, 0.72f, 0.28f, 1f); private static readonly Quaternion CreatureIconRotation = Quaternion.Euler(23f, 51f, 25.8f); private static readonly HashSet PendingItems = new HashSet(); private static readonly Dictionary> CreatureIconVariants = new Dictionary>(new SharedDataReferenceComparer()); private static readonly HashSet CreatureIconFailures = new HashSet(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary> IconComponentDependencies = new Dictionary>(); private static readonly HashSet NonTamingOrbSharedData = new HashSet(); private static readonly int OdinHorseHaveArmorHash = StringExtensionMethods.GetStableHashCode("HaveArmor"); private static readonly int OdinHorseSetArmorRpcHash = StringExtensionMethods.GetStableHashCode("SetArmor"); private static bool _cllcApiResolved; private static bool _cllcApiAvailable; private static Type? _cllcInfusionType; private static Type? _cllcExtraEffectType; private static MethodInfo? _cllcGetInfusionCreature; private static MethodInfo? _cllcGetExtraEffectCreature; private static MethodInfo? _cllcSetInfusionCreature; private static MethodInfo? _cllcSetExtraEffectCreature; private static readonly int ColorProperty = Shader.PropertyToID("_Color"); private static readonly int TintColorProperty = Shader.PropertyToID("_TintColor"); private static readonly int EmissionColorProperty = Shader.PropertyToID("_EmissionColor"); private static readonly int ModeProperty = Shader.PropertyToID("_Mode"); private static readonly int SrcBlendProperty = Shader.PropertyToID("_SrcBlend"); private static readonly int DstBlendProperty = Shader.PropertyToID("_DstBlend"); private static readonly int ZWriteProperty = Shader.PropertyToID("_ZWrite"); private static readonly int StarProgressHash = StringExtensionMethods.GetStableHashCode("flg_star_progress"); private static readonly int GrowthElapsedHash = StringExtensionMethods.GetStableHashCode("flg_growth_elapsed"); private static readonly int GrowthLastEvalHash = StringExtensionMethods.GetStableHashCode("flg_growth_last_eval"); private static readonly int ReleaseRaycastMask = LayerMask.GetMask(new string[12] { "Default", "static_solid", "Default_small", "piece", "piece_nonsolid", "terrain", "character", "character_net", "character_ghost", "hitbox", "character_noenv", "vehicle" }); private static GameObject? _prefab; private static GameObject? _prefabContainer; private static Sprite? _icon; private static Camera? _iconCamera; private static Light? _iconLight; private static SharedData? _tamingOrbSharedData; internal static void Shutdown() { PendingItems.Clear(); CreatureIconVariants.Clear(); CreatureIconFailures.Clear(); IconComponentDependencies.Clear(); NonTamingOrbSharedData.Clear(); if ((Object)(object)_iconCamera != (Object)null) { Object.Destroy((Object)(object)((Component)_iconCamera).gameObject); _iconCamera = null; } if ((Object)(object)_iconLight != (Object)null) { Object.Destroy((Object)(object)((Component)_iconLight).gameObject); _iconLight = null; } _tamingOrbSharedData = null; } internal static void OnGameDataChanged() { CreatureIconFailures.Clear(); } internal static void RegisterPrefabsAndRecipes(ObjectDB? objectDb = null) { if (objectDb == null) { objectDb = ObjectDB.instance; } if (objectDb == null) { return; } objectDb.m_recipes.RemoveAll((Recipe recipe) => (Object)(object)recipe != (Object)null && ((Object)recipe).name == "Recipe_FLG_TamingOrb"); if (!IsEnabled()) { return; } GameObject val = EnsurePrefab(objectDb); if (val != null) { RegisterWithObjectDb(objectDb, val); if (ZNetScene.instance != null) { RegisterWithZNetScene(ZNetScene.instance, val); } RegisterRecipe(objectDb, val); } } internal static bool TryHandleTameableUseItem(Tameable tameable, Humanoid user, ItemData item) { if (!IsTamingOrb(item)) { return false; } Player val = (Player)(object)((user is Player) ? user : null); if (val == null) { return true; } if (!IsEnabled()) { FeedLocalization.Message(val, "$flg_taming_orb_disabled"); return true; } if (TryReadPayload(item, out CapturedTamePayload _)) { TryRelease(val, item); return true; } TryCapture(val, tameable, item); return true; } internal static bool TryHandlePlayerUseItem(Player player, ItemData item) { if ((Object)(object)player == (Object)null || !IsTamingOrb(item)) { return false; } if (!IsEnabled()) { FeedLocalization.Message(player, "$flg_taming_orb_disabled"); return true; } if (TryReadPayload(item, out CapturedTamePayload _)) { TryRelease(player, item); return true; } GameObject hoverObject = ((Humanoid)player).GetHoverObject(); Tameable val = (((Object)(object)hoverObject == (Object)null) ? null : hoverObject.GetComponentInParent()); if ((Object)(object)val == (Object)null) { FeedLocalization.Message(player, "$flg_taming_orb_use_on_tamed"); return true; } TryCapture(player, val, item); return true; } internal static void AppendTooltip(ItemData item, bool crafting, ref string tooltip) { if (!crafting && IsTamingOrb(item)) { if (TryReadPayload(item, out CapturedTamePayload payload)) { string payloadDisplayName = GetPayloadDisplayName(payload); tooltip = tooltip + "\n\n" + FeedLocalization.Format("$flg_taming_orb_tooltip_captured", payloadDisplayName) + "\n" + FeedLocalization.Format("$flg_taming_orb_tooltip_level", payload.Level.ToString(CultureInfo.InvariantCulture)) + "\n" + FeedLocalization.Format("$flg_taming_orb_tooltip_hp", payload.Health.ToString("0.#", CultureInfo.InvariantCulture), payload.MaxHealth.ToString("0.#", CultureInfo.InvariantCulture)) + "\n" + FeedLocalization.Text("$flg_taming_orb_tooltip_release"); } else if (item.m_customData.ContainsKey("flg.tame.payload")) { tooltip = tooltip + "\n\n" + FeedLocalization.Text("$flg_taming_orb_tooltip_invalid"); } else { tooltip = tooltip + "\n\n" + FeedLocalization.Text("$flg_taming_orb_tooltip_empty") + "\n" + FeedLocalization.Text("$flg_taming_orb_tooltip_capture"); } } } internal static string AugmentDroppedItemHoverText(ItemDrop itemDrop, string originalText) { if (itemDrop == null || !IsTamingOrb(itemDrop.m_itemData)) { return originalText; } if (!TryReadPayload(itemDrop.m_itemData, out CapturedTamePayload payload)) { return originalText; } return originalText + " (" + GetPayloadDisplayName(payload) + ")"; } private static bool TryCapture(Player player, Tameable tameable, ItemData item) { //IL_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: Unknown result type (might be due to invalid IL or missing references) //IL_00fe: Unknown result type (might be due to invalid IL or missing references) //IL_0109: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)tameable == (Object)null) { FeedLocalization.Message(player, "$flg_taming_orb_no_tameable"); return false; } if (PendingItems.Contains(item)) { FeedLocalization.Message(player, "$flg_taming_orb_pending"); return true; } Character component = ((Component)tameable).GetComponent(); ZNetView component2 = ((Component)tameable).GetComponent(); if ((Object)(object)component == (Object)null || (Object)(object)component2 == (Object)null || !component2.IsValid()) { FeedLocalization.Message(player, "$flg_taming_orb_cannot_capture"); return false; } if (component.IsDead()) { FeedLocalization.Message(player, "$flg_taming_orb_dead"); return false; } if (!component.IsTamed()) { FeedLocalization.Message(player, "$flg_taming_orb_only_tamed"); return false; } if (Vector3.Distance(((Component)player).transform.position, ((Component)component).transform.position) > 5f) { FeedLocalization.Message(player, "$flg_taming_orb_too_far"); return false; } if (!TryGetPrefabName(((Component)component).gameObject, out string prefabName)) { FeedLocalization.Message(player, "$flg_taming_orb_prefab_missing"); return false; } PlayActionEffect(component.GetCenterPoint(), ((Component)component).transform.rotation); PendingItems.Add(item); FeedLocalization.Message(player, "$flg_taming_orb_capturing", component.GetHoverName()); ((MonoBehaviour)FeedLikeGrandmaPlugin.Instance).StartCoroutine(CompleteCaptureAfterDelay(player, tameable, component, component2, item, prefabName, 4.5f)); return true; } private static IEnumerator CompleteCaptureAfterDelay(Player player, Tameable tameable, Character character, ZNetView nview, ItemData item, string creaturePrefabName, float delay) { yield return (object)new WaitForSeconds(delay); try { CompleteCapture(player, tameable, character, nview, item, creaturePrefabName); } finally { PendingItems.Remove(item); } } private static bool CompleteCapture(Player player, Tameable tameable, Character character, ZNetView nview, ItemData item, string creaturePrefabName) { if ((Object)(object)player == (Object)null || !((Humanoid)player).GetInventory().ContainsItem(item)) { return false; } if ((Object)(object)character == (Object)null || (Object)(object)tameable == (Object)null || (Object)(object)nview == (Object)null || !nview.IsValid() || character.IsDead() || !character.IsTamed()) { FeedLocalization.Message(player, "$flg_taming_orb_no_longer_capturable"); return false; } CapturedTamePayload capturedTamePayload = CapturePayload(character, tameable, nview, creaturePrefabName); item.m_stack = 1; item.m_customData["flg.tame.payload"] = capturedTamePayload.Serialize(); item.m_customData["flg.tame.icon_prefab"] = capturedTamePayload.Prefab; item.m_variant = GetIconVariantForPayload(item, capturedTamePayload); ((Humanoid)player).GetInventory().Changed(); nview.ClaimOwnership(); nview.Destroy(); FeedLocalization.Message(player, "$flg_taming_orb_captured", GetPayloadDisplayName(capturedTamePayload)); return true; } private static bool TryRelease(Player player, ItemData item) { //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: Unknown result type (might be due to invalid IL or missing references) if (!TryReadPayload(item, out CapturedTamePayload payload)) { FeedLocalization.Message(player, "$flg_taming_orb_data_invalid"); return false; } if (PendingItems.Contains(item)) { FeedLocalization.Message(player, "$flg_taming_orb_pending"); return true; } if (ZNetScene.instance == null) { FeedLocalization.Message(player, "$flg_taming_orb_prefab_database_not_ready"); return false; } GameObject prefab = ZNetScene.instance.GetPrefab(payload.Prefab); if (prefab == null) { FeedLocalization.Message(player, "$flg_taming_orb_prefab_named_missing", payload.Prefab); return false; } GetReleaseTransform(player, out var position, out var rotation); PlayActionEffect(position, rotation); PendingItems.Add(item); FeedLocalization.Message(player, "$flg_taming_orb_releasing", GetPayloadDisplayName(payload)); ((MonoBehaviour)FeedLikeGrandmaPlugin.Instance).StartCoroutine(CompleteReleaseAfterDelay(player, item, payload, prefab, position, rotation, 4.5f)); return true; } private static IEnumerator CompleteReleaseAfterDelay(Player player, ItemData item, CapturedTamePayload payload, GameObject creaturePrefab, Vector3 spawnPosition, Quaternion spawnRotation, float delay) { //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) yield return (object)new WaitForSeconds(delay); try { CompleteRelease(player, item, payload, creaturePrefab, spawnPosition, spawnRotation); } finally { PendingItems.Remove(item); } } private static bool CompleteRelease(Player player, ItemData item, CapturedTamePayload payload, GameObject creaturePrefab, Vector3 spawnPosition, Quaternion spawnRotation) { //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)player == (Object)null || !((Humanoid)player).GetInventory().ContainsItem(item)) { return false; } if (!TryReadPayload(item, out CapturedTamePayload payload2) || payload2.Serialize() != payload.Serialize()) { FeedLocalization.Message(player, "$flg_taming_orb_data_changed"); return false; } GameObject val = null; try { val = Object.Instantiate(creaturePrefab, spawnPosition, spawnRotation); Character component = val.GetComponent(); Tameable component2 = val.GetComponent(); ZNetView component3 = val.GetComponent(); if ((Object)(object)component == (Object)null || (Object)(object)component3 == (Object)null || !component3.IsValid()) { DestroySpawnedRelease(val); FeedLocalization.Message(player, "$flg_taming_orb_invalid_creature"); return false; } RestorePayload(payload, component, component2, component3); item.m_customData.Remove("flg.tame.payload"); item.m_customData.Remove("flg.tame.icon_prefab"); item.m_variant = 0; ((Humanoid)player).GetInventory().Changed(); FeedLocalization.Message(player, "$flg_taming_orb_released", GetPayloadDisplayName(payload)); return true; } catch (Exception ex) { if (val != null) { DestroySpawnedRelease(val); } FeedLikeGrandmaPlugin.Log.LogWarning((object)("FLG_TamingOrb failed to release " + payload.Prefab + ": " + ex.Message)); FeedLocalization.Message(player, "$flg_taming_orb_release_failed"); return false; } } private static bool IsEnabled() { return !string.IsNullOrWhiteSpace(FeedLikeGrandmaPlugin.TamingOrbRecipe.Value); } private static void PlayActionEffect(Vector3 position, Quaternion rotation) { //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) GameObject val = (((Object)(object)ZNetScene.instance == (Object)null) ? null : ZNetScene.instance.GetPrefab("fx_summon_start")); if (val == null) { FeedLikeGrandmaPlugin.Log.LogWarning((object)"FLG_TamingOrb effect prefab 'fx_summon_start' was not found."); } else { Object.Instantiate(val, position, rotation); } } private static CapturedTamePayload CapturePayload(Character character, Tameable tameable, ZNetView nview, string creaturePrefabName) { ZDO zDO = nview.GetZDO(); long ticks = ZNet.instance.GetTime().Ticks; long num = zDO.GetLong(ZDOVars.s_tameLastFeeding, 0L); long num2 = zDO.GetLong(ZDOVars.s_spawnTime, 0L); CaptureCllcApiPayload(character, out string infusion, out string extraEffect); Sadle saddle; return new CapturedTamePayload { Prefab = creaturePrefabName, Name = zDO.GetString(ZDOVars.s_tamedName, string.Empty), Level = Mathf.Max(1, character.GetLevel()), Health = Mathf.Max(1f, character.GetHealth()), MaxHealth = Mathf.Max(1f, character.GetMaxHealth()), HasSaddle = tameable.HaveSaddle(), Inventory = zDO.GetString(ZDOVars.s_items, string.Empty), LastFedAgeTicks = ((num > 0) ? Math.Max(0L, ticks - num) : 0), SpawnAgeTicks = ((num2 > 0) ? Math.Max(0L, ticks - num2) : 0), StarProgress = Mathf.Max(0f, zDO.GetFloat(StarProgressHash, 0f)), GrowthElapsed = zDO.GetFloat(GrowthElapsedHash, -1f), CllcEffect = zDO.GetInt("CL&LC effect", 0), CllcInfusion = infusion, CllcExtraEffect = extraEffect, OdinHorseHasArmor = (LooksLikeOdinHorse(tameable) && zDO.GetBool(OdinHorseHaveArmorHash, false)), SaddleStamina = (TryGetSaddle(character, tameable, out saddle) ? Mathf.Clamp(saddle.GetStamina(), 0f, saddle.GetMaxStamina()) : (-1f)) }; } private static void RestorePayload(CapturedTamePayload payload, Character character, Tameable? tameable, ZNetView nview) { ZDO zDO = nview.GetZDO(); long ticks = ZNet.instance.GetTime().Ticks; if (payload.CllcEffect != 0) { zDO.Set("CL&LC effect", payload.CllcEffect); } if (!string.IsNullOrEmpty(payload.Inventory)) { zDO.Set(ZDOVars.s_items, payload.Inventory); } if (payload.LastFedAgeTicks > 0) { zDO.Set(ZDOVars.s_tameLastFeeding, ticks - payload.LastFedAgeTicks); } if (payload.SpawnAgeTicks > 0) { zDO.Set(ZDOVars.s_spawnTime, ticks - payload.SpawnAgeTicks); } if (payload.StarProgress > 0f) { zDO.Set(StarProgressHash, payload.StarProgress); } if (payload.GrowthElapsed >= 0f) { zDO.Set(GrowthElapsedHash, payload.GrowthElapsed); zDO.Set(GrowthLastEvalHash, ticks); } character.SetTamed(true); character.SetLevel(Mathf.Max(1, payload.Level)); character.SetMaxHealth(Mathf.Max(1f, payload.MaxHealth)); character.SetHealth(Mathf.Clamp(payload.Health, 1f, Mathf.Max(1f, payload.MaxHealth))); RestoreCllcApiPayload(payload, character); if ((Object)(object)tameable != (Object)null) { if (!string.IsNullOrWhiteSpace(payload.Name)) { tameable.RPC_SetName(0L, payload.Name, string.Empty); } if (payload.HasSaddle) { zDO.Set(ZDOVars.s_haveSaddleHash, true); tameable.SetSaddle(true); } RestoreOdinHorseArmor(payload, tameable, nview, zDO); } if (payload.SaddleStamina >= 0f && TryGetSaddle(character, tameable, out Sadle saddle)) { zDO.Set(ZDOVars.s_stamina, Mathf.Clamp(payload.SaddleStamina, 0f, saddle.GetMaxStamina())); } } private static void GetReleaseTransform(Player player, out Vector3 position, out Quaternion rotation) { //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_004e: 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_005d: 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_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Unknown result type (might be due to invalid IL or missing references) Camera val = ((GameCamera.instance != null) ? GameCamera.instance.m_camera : null); Vector3 forward = ((val != null) ? ((Component)val).transform : ((Component)player).transform).forward; RaycastHit val2 = default(RaycastHit); if (Physics.Raycast(((Character)player).GetEyePoint(), forward, ref val2, 5f, ReleaseRaycastMask)) { position = ((RaycastHit)(ref val2)).point + Vector3.up * 1f; } else { position = ((Component)player).transform.position + forward * 1f + Vector3.up * 1f; } rotation = Quaternion.identity; } private static void DestroySpawnedRelease(GameObject spawned) { ZNetView component = spawned.GetComponent(); if ((Object)(object)component != (Object)null && component.IsValid()) { component.ClaimOwnership(); component.Destroy(); } else { Object.Destroy((Object)(object)spawned); } } private static bool TryGetSaddle(Character character, Tameable? tameable, out Sadle saddle) { saddle = null; if ((Object)(object)tameable != (Object)null && (Object)(object)tameable.m_saddle != (Object)null) { saddle = tameable.m_saddle; return true; } Sadle componentInChildren = ((Component)character).GetComponentInChildren(); if (componentInChildren == null) { return false; } saddle = componentInChildren; return true; } private static void CaptureCllcApiPayload(Character character, out string infusion, out string extraEffect) { infusion = string.Empty; extraEffect = string.Empty; if ((Object)(object)character == (Object)null || !TryResolveCllcApi()) { return; } try { infusion = _cllcGetInfusionCreature?.Invoke(null, new object[1] { character })?.ToString() ?? string.Empty; extraEffect = _cllcGetExtraEffectCreature?.Invoke(null, new object[1] { character })?.ToString() ?? string.Empty; } catch (Exception ex) { FeedLikeGrandmaPlugin.Log.LogDebug((object)("Could not capture CL&LC data for " + ((Object)character).name + ": " + ex.Message)); infusion = string.Empty; extraEffect = string.Empty; } } private static void RestoreCllcApiPayload(CapturedTamePayload payload, Character character) { if (!((Object)(object)character == (Object)null) && TryResolveCllcApi()) { TrySetCllcEnum(_cllcSetInfusionCreature, _cllcInfusionType, character, payload.CllcInfusion, "infusion"); TrySetCllcEnum(_cllcSetExtraEffectCreature, _cllcExtraEffectType, character, payload.CllcExtraEffect, "extra effect"); } } private static void TrySetCllcEnum(MethodInfo? method, Type? enumType, Character character, string value, string label) { if (method == null || enumType == null || string.IsNullOrWhiteSpace(value)) { return; } try { object obj = Enum.Parse(enumType, value, ignoreCase: true); method.Invoke(null, new object[2] { character, obj }); } catch (Exception ex) { FeedLikeGrandmaPlugin.Log.LogDebug((object)("Could not restore CL&LC " + label + " '" + value + "' for " + ((Object)character).name + ": " + ex.Message)); } } private static bool TryResolveCllcApi() { if (_cllcApiResolved) { return _cllcApiAvailable; } _cllcApiResolved = true; try { Assembly? assembly = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault((Assembly loaded) => loaded.GetName().Name == "CreatureLevelControl"); Type type = assembly?.GetType("CreatureLevelControl.API"); _cllcInfusionType = assembly?.GetType("CreatureLevelControl.CreatureInfusion"); _cllcExtraEffectType = assembly?.GetType("CreatureLevelControl.CreatureExtraEffect"); if (type == null || _cllcInfusionType == null || _cllcExtraEffectType == null) { _cllcApiAvailable = false; return false; } _cllcGetInfusionCreature = FindCllcGetter(type, "GetInfusionCreature"); _cllcGetExtraEffectCreature = FindCllcGetter(type, "GetExtraEffectCreature"); _cllcSetInfusionCreature = FindCllcSetter(type, "SetInfusionCreature", _cllcInfusionType); _cllcSetExtraEffectCreature = FindCllcSetter(type, "SetExtraEffectCreature", _cllcExtraEffectType); _cllcApiAvailable = _cllcGetInfusionCreature != null && _cllcGetExtraEffectCreature != null && _cllcSetInfusionCreature != null && _cllcSetExtraEffectCreature != null; return _cllcApiAvailable; } catch (Exception ex) { FeedLikeGrandmaPlugin.Log.LogDebug((object)("Could not resolve CL&LC API: " + ex.Message)); _cllcApiAvailable = false; return false; } } private static MethodInfo? FindCllcGetter(Type apiType, string name) { return apiType.GetMethods(BindingFlags.Static | BindingFlags.Public).FirstOrDefault(delegate(MethodInfo method) { if (method.Name != name) { return false; } ParameterInfo[] parameters = method.GetParameters(); return parameters.Length == 1 && parameters[0].ParameterType.IsAssignableFrom(typeof(Character)); }); } private static MethodInfo? FindCllcSetter(Type apiType, string name, Type enumType) { return apiType.GetMethods(BindingFlags.Static | BindingFlags.Public).FirstOrDefault(delegate(MethodInfo method) { if (method.Name != name) { return false; } ParameterInfo[] parameters = method.GetParameters(); return parameters.Length == 2 && parameters[0].ParameterType.IsAssignableFrom(typeof(Character)) && parameters[1].ParameterType == enumType; }); } private static void RestoreOdinHorseArmor(CapturedTamePayload payload, Tameable tameable, ZNetView nview, ZDO zdo) { if (!payload.OdinHorseHasArmor || (Object)(object)tameable == (Object)null || (Object)(object)nview == (Object)null || !nview.IsValid() || !LooksLikeOdinHorse(tameable)) { return; } zdo.Set(OdinHorseHaveArmorHash, true); if (nview.m_functions != null && nview.m_functions.ContainsKey(OdinHorseSetArmorRpcHash)) { try { nview.InvokeRPC(ZNetView.Everybody, "SetArmor", new object[1] { true }); } catch (Exception ex) { FeedLikeGrandmaPlugin.Log.LogDebug((object)("Could not invoke OdinHorse SetArmor RPC for " + ((Object)tameable).name + ": " + ex.Message)); } } TrySetOdinHorseArmorObjects(tameable, enabled: true); } private static bool LooksLikeOdinHorse(Tameable? tameable) { if ((Object)(object)tameable == (Object)null) { return false; } return ((Object)((Component)((Component)tameable).transform.root).gameObject).name.IndexOf("rae_OdinHorse", StringComparison.OrdinalIgnoreCase) >= 0; } private static bool TrySetOdinHorseArmorObjects(Tameable tameable, bool enabled) { try { bool num = TrySetChildActive(((Component)tameable).transform, "Horse_mask", enabled); bool flag = TrySetChildActive(((Component)tameable).transform, "horse_armor", enabled); return num || flag; } catch (Exception ex) { FeedLikeGrandmaPlugin.Log.LogDebug((object)("Could not set OdinHorse armor visuals for " + ((Object)tameable).name + ": " + ex.Message)); return false; } } private static bool TrySetChildActive(Transform root, string childName, bool enabled) { Transform val = FindChildRecursive(root, childName); if (val == null) { return false; } ((Component)val).gameObject.SetActive(enabled); return true; } private static Transform? FindChildRecursive(Transform root, string childName) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Expected O, but got Unknown foreach (Transform item in root) { Transform val = item; if (((Object)val).name == childName) { return val; } Transform val2 = FindChildRecursive(val, childName); if (val2 != null) { return val2; } } return null; } private static bool TryReadPayload(ItemData item, out CapturedTamePayload? payload, bool logWarnings = true) { payload = null; if (item == null || !item.m_customData.TryGetValue("flg.tame.payload", out var value) || string.IsNullOrWhiteSpace(value)) { return false; } return CapturedTamePayload.TryDeserialize(value, out payload, logWarnings); } private static string GetPayloadDisplayName(CapturedTamePayload payload) { if (!string.IsNullOrWhiteSpace(payload.Name)) { return payload.Name; } GameObject val = (((Object)(object)ZNetScene.instance == (Object)null) ? null : ZNetScene.instance.GetPrefab(payload.Prefab)); Character val2 = (((Object)(object)val == (Object)null) ? null : val.GetComponent()); if ((Object)(object)val2 != (Object)null && Localization.instance != null) { return Localization.instance.Localize(val2.m_name); } return payload.Prefab; } private static GameObject? EnsurePrefab(ObjectDB objectDb) { if (_prefab != null) { return _prefab; } GameObject val = FindSpawnableItemPrefab(objectDb, "DragonTear"); if (val == null) { FeedLikeGrandmaPlugin.Log.LogDebug((object)"Could not create FLG_TamingOrb yet: source item 'DragonTear' was not found."); return null; } if (!HasSpawnableItemShape(val)) { FeedLikeGrandmaPlugin.Log.LogDebug((object)"Could not create FLG_TamingOrb yet: source item 'DragonTear' is not a complete spawnable item prefab."); return null; } GameObject val2 = Object.Instantiate(val, EnsurePrefabContainer().transform); ((Object)val2).name = "FLG_TamingOrb"; ItemDrop component = val2.GetComponent(); if (component == null) { FeedLikeGrandmaPlugin.Log.LogWarning((object)"Could not create FLG_TamingOrb: source item has no ItemDrop component."); Object.Destroy((Object)(object)val2); return null; } ConfigureItemData(component); ConfigureVisual(val2, component); _prefab = val2; return _prefab; } private static GameObject EnsurePrefabContainer() { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Expected O, but got Unknown if (_prefabContainer != null) { return _prefabContainer; } _prefabContainer = new GameObject("FeedLikeGrandma Prefabs"); _prefabContainer.SetActive(false); Object.DontDestroyOnLoad((Object)(object)_prefabContainer); return _prefabContainer; } private static void ConfigureItemData(ItemDrop itemDrop) { //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) SharedData shared = itemDrop.m_itemData.m_shared; shared.m_name = "$flg_taming_orb"; shared.m_description = "$flg_taming_orb_description"; shared.m_maxStackSize = 1; shared.m_weight = 1f; shared.m_itemType = (ItemType)16; shared.m_attachOverride = (ItemType)0; shared.m_useDurability = false; shared.m_maxDurability = 0f; shared.m_canBeReparied = false; shared.m_destroyBroken = false; Sprite[] icons = shared.m_icons; Sprite source = ((icons != null && icons.Length > 0) ? shared.m_icons[0] : null); if (_icon == null) { _icon = CreateTintedIcon(source, TamingOrbGreen); } if (_icon != null) { shared.m_icons = (Sprite[])(object)new Sprite[1] { _icon }; } CreatureIconVariants.Remove(shared); shared.m_variants = 1; itemDrop.m_itemData.m_variant = 0; _tamingOrbSharedData = shared; NonTamingOrbSharedData.Remove(shared); } private static void ConfigureVisual(GameObject prefab, ItemDrop itemDrop) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) EnsureRootZNetView(prefab); TintDragonTearCoreMeshes(prefab, TamingOrbGreen); itemDrop.m_itemData.m_dropPrefab = prefab; itemDrop.m_itemData.m_variant = 0; } internal static void NormalizeVariant(ItemData item) { if (!IsTamingOrb(item)) { return; } if (!item.m_customData.ContainsKey("flg.tame.payload")) { item.m_customData.Remove("flg.tame.icon_prefab"); item.m_variant = 0; return; } if (!TryReadPayload(item, out CapturedTamePayload payload, logWarnings: false)) { item.m_customData.Remove("flg.tame.icon_prefab"); item.m_variant = 0; return; } string prefab = payload.Prefab; if (!item.m_customData.TryGetValue("flg.tame.icon_prefab", out var value) || !value.Equals(prefab, StringComparison.OrdinalIgnoreCase) || !TryGetCreatureIconVariant(item.m_shared, prefab, out var variant) || item.m_variant != variant || !IsIconVariantValid(item.m_shared, variant)) { item.m_customData["flg.tame.icon_prefab"] = prefab; item.m_variant = GetIconVariantForPayload(item, payload); } } private static int GetIconVariantForPayload(ItemData item, CapturedTamePayload payload) { if (item == null || item.m_shared == null || payload == null || string.IsNullOrWhiteSpace(payload.Prefab)) { return 0; } return EnsureCreatureIconVariant(item.m_shared, payload.Prefab); } private static int EnsureCreatureIconVariant(SharedData shared, string creaturePrefabName) { if (string.IsNullOrWhiteSpace(creaturePrefabName)) { return 0; } Dictionary creatureIconVariants = GetCreatureIconVariants(shared); if (creatureIconVariants.TryGetValue(creaturePrefabName, out var value) && IsIconVariantValid(shared, value)) { return value; } creatureIconVariants.Remove(creaturePrefabName); if (CreatureIconFailures.Contains(creaturePrefabName)) { return 0; } GameObject val = (((Object)(object)ZNetScene.instance == (Object)null) ? null : ZNetScene.instance.GetPrefab(creaturePrefabName)); if (val == null) { CreatureIconFailures.Add(creaturePrefabName); return 0; } Sprite val2 = TryLoadCreatureIconFromCache(creaturePrefabName); if (val2 == null) { Sprite val3 = CreateCreatureIcon(val); try { val2 = CreateCompositeTamingOrbIcon(val3); } finally { if (val3 != null) { Texture2D texture = val3.texture; Object.Destroy((Object)(object)val3); if (texture != null) { Object.Destroy((Object)(object)texture); } } } if (val2 != null) { TrySaveCreatureIconToCache(creaturePrefabName, val2); } } if (val2 == null) { CreatureIconFailures.Add(creaturePrefabName); return 0; } return creatureIconVariants[creaturePrefabName] = AppendInternalIconVariant(shared, val2); } private static Dictionary GetCreatureIconVariants(SharedData shared) { if (!CreatureIconVariants.TryGetValue(shared, out Dictionary value)) { value = new Dictionary(StringComparer.OrdinalIgnoreCase); CreatureIconVariants[shared] = value; } return value; } private static bool TryGetCreatureIconVariant(SharedData shared, string creaturePrefabName, out int variant) { variant = 0; if (!string.IsNullOrWhiteSpace(creaturePrefabName) && CreatureIconVariants.TryGetValue(shared, out Dictionary value) && value.TryGetValue(creaturePrefabName, out variant)) { return IsIconVariantValid(shared, variant); } return false; } private static bool IsIconVariantValid(SharedData shared, int variant) { if (variant > 0 && shared.m_icons != null && variant < shared.m_icons.Length) { return (Object)(object)shared.m_icons[variant] != (Object)null; } return false; } private static int AppendInternalIconVariant(SharedData shared, Sprite icon) { Sprite[] array = shared.m_icons ?? Array.Empty(); if (array.Length == 0 && _icon != null) { array = (Sprite[])(object)new Sprite[1] { _icon }; } int num = array.Length; Array.Resize(ref array, num + 1); array[num] = icon; shared.m_icons = array; shared.m_variants = 1; return num; } private static Sprite? TryLoadCreatureIconFromCache(string creaturePrefabName) { //IL_00ab: 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_00c7: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: Unknown result type (might be due to invalid IL or missing references) //IL_00d3: Unknown result type (might be due to invalid IL or missing references) //IL_00da: Unknown result type (might be due to invalid IL or missing references) //IL_00f2: Expected O, but got Unknown //IL_011e: Unknown result type (might be due to invalid IL or missing references) //IL_012d: Unknown result type (might be due to invalid IL or missing references) string creatureIconCachePath = GetCreatureIconCachePath(creaturePrefabName); if (!File.Exists(creatureIconCachePath)) { return null; } try { using FileStream input = File.Open(creatureIconCachePath, FileMode.Open, FileAccess.Read, FileShare.Read); using BinaryReader binaryReader = new BinaryReader(input); string text = binaryReader.ReadString(); int num = binaryReader.ReadInt32(); int num2 = binaryReader.ReadInt32(); int num3 = binaryReader.ReadInt32(); if (text != "FLGICON" || num != 1 || num2 <= 0 || num3 <= 0 || num2 > 1024 || num3 > 1024) { TryDeleteIconCache(creatureIconCachePath); return null; } int num4 = checked(num2 * num3); Color32[] array = (Color32[])(object)new Color32[num4]; for (int i = 0; i < num4; i++) { array[i] = new Color32(binaryReader.ReadByte(), binaryReader.ReadByte(), binaryReader.ReadByte(), binaryReader.ReadByte()); } Texture2D val = new Texture2D(num2, num3, (TextureFormat)4, false) { filterMode = (FilterMode)1, wrapMode = (TextureWrapMode)1, name = "FLG_" + creaturePrefabName + "_CachedIcon" }; val.SetPixels32(array); val.Apply(); return Sprite.Create(val, new Rect(0f, 0f, (float)((Texture)val).width, (float)((Texture)val).height), new Vector2(0.5f, 0.5f)); } catch (Exception ex) { FeedLikeGrandmaPlugin.Log.LogDebug((object)("Could not load cached FLG_TamingOrb creature icon for " + creaturePrefabName + ": " + ex.Message)); TryDeleteIconCache(creatureIconCachePath); return null; } } private static void TrySaveCreatureIconToCache(string creaturePrefabName, Sprite icon) { //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: 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) if (icon == null || icon.texture == null) { return; } try { Directory.CreateDirectory(GetCreatureIconCacheDirectory()); Color32[] pixels = icon.texture.GetPixels32(); if (pixels.Length == 0) { return; } using FileStream output = File.Open(GetCreatureIconCachePath(creaturePrefabName), FileMode.Create, FileAccess.Write, FileShare.Read); using BinaryWriter binaryWriter = new BinaryWriter(output); binaryWriter.Write("FLGICON"); binaryWriter.Write(1); binaryWriter.Write(((Texture)icon.texture).width); binaryWriter.Write(((Texture)icon.texture).height); Color32[] array = pixels; foreach (Color32 val in array) { binaryWriter.Write(val.r); binaryWriter.Write(val.g); binaryWriter.Write(val.b); binaryWriter.Write(val.a); } } catch (Exception ex) { FeedLikeGrandmaPlugin.Log.LogDebug((object)("Could not cache FLG_TamingOrb creature icon for " + creaturePrefabName + ": " + ex.Message)); } } private static void TryDeleteIconCache(string cachePath) { try { if (File.Exists(cachePath)) { File.Delete(cachePath); } } catch { } } private static string GetCreatureIconCacheDirectory() { return Path.Combine(FeedLikeGrandmaPlugin.ConfigDirectoryPath, "cache", "icons"); } private static string GetCreatureIconCachePath(string creaturePrefabName) { string text = SanitizeCacheFileName(creaturePrefabName); uint stableHashCode = (uint)StringExtensionMethods.GetStableHashCode(creaturePrefabName); return Path.Combine(GetCreatureIconCacheDirectory(), string.Format("{0}-{1:X8}-{2}-{3}.flgicon", text, stableHashCode, "1.0.0", "raw-v4-composite78-border2")); } private static string SanitizeCacheFileName(string name) { if (string.IsNullOrWhiteSpace(name)) { return "creature"; } HashSet invalid = new HashSet(Path.GetInvalidFileNameChars()); string text = new string(name.Select((char character) => (!invalid.Contains(character) && !char.IsWhiteSpace(character)) ? character : '_').ToArray()).Trim(new char[1] { '_' }); if (string.IsNullOrWhiteSpace(text)) { text = "creature"; } if (text.Length > 80) { return text.Substring(0, 80); } return text; } internal static bool ShouldSkipInventoryCloneWorldAwake(Component component) { if (!ZNetView.m_forceDisableInit || component == null) { return false; } GameObject gameObject = component.gameObject; if (((Object)gameObject).name.StartsWith("FLG_TamingOrb", StringComparison.Ordinal)) { return true; } ItemDrop component2 = gameObject.GetComponent(); if (component2 != null) { return IsTamingOrb(component2.m_itemData); } return false; } private static void EnsureRootZNetView(GameObject prefab) { if (prefab.GetComponent() == null) { prefab.AddComponent().m_persistent = true; FeedLikeGrandmaPlugin.Log.LogDebug((object)"FLG_TamingOrb: added missing root ZNetView to cloned item prefab."); } } private static void RegisterWithObjectDb(ObjectDB objectDb, GameObject prefab) { int stableHashCode = StringExtensionMethods.GetStableHashCode("FLG_TamingOrb"); if (objectDb.m_items.All((GameObject item) => (Object)(object)item == (Object)null || ((Object)item).name != "FLG_TamingOrb")) { objectDb.m_items.Add(prefab); } if (!objectDb.m_itemByHash.ContainsKey(stableHashCode)) { objectDb.m_itemByHash.Add(stableHashCode, prefab); } else if ((Object)(object)objectDb.m_itemByHash[stableHashCode] == (Object)null || ((Object)objectDb.m_itemByHash[stableHashCode]).name == "FLG_TamingOrb") { objectDb.m_itemByHash[stableHashCode] = prefab; } ItemDrop component = prefab.GetComponent(); if (component != null) { objectDb.m_itemByData[component.m_itemData.m_shared] = prefab; } } private static void RegisterWithZNetScene(ZNetScene zNetScene, GameObject prefab) { int stableHashCode = StringExtensionMethods.GetStableHashCode("FLG_TamingOrb"); if ((Object)(object)prefab.GetComponent() != (Object)null) { if (zNetScene.m_prefabs.All((GameObject existing) => (Object)(object)existing == (Object)null || ((Object)existing).name != "FLG_TamingOrb")) { zNetScene.m_prefabs.Add(prefab); } } else if (zNetScene.m_nonNetViewPrefabs.All((GameObject existing) => (Object)(object)existing == (Object)null || ((Object)existing).name != "FLG_TamingOrb")) { zNetScene.m_nonNetViewPrefabs.Add(prefab); } if (!zNetScene.m_namedPrefabs.ContainsKey(stableHashCode)) { zNetScene.m_namedPrefabs.Add(stableHashCode, prefab); } else if ((Object)(object)zNetScene.m_namedPrefabs[stableHashCode] == (Object)null || ((Object)zNetScene.m_namedPrefabs[stableHashCode]).name == "FLG_TamingOrb") { zNetScene.m_namedPrefabs[stableHashCode] = prefab; } } private static void RegisterRecipe(ObjectDB objectDb, GameObject prefab) { ItemDrop component = prefab.GetComponent(); if (component != null) { objectDb.m_recipes.RemoveAll((Recipe recipe) => (Object)(object)recipe != (Object)null && ((Object)recipe).name == "Recipe_FLG_TamingOrb"); Recipe val = ScriptableObject.CreateInstance(); ((Object)val).name = "Recipe_FLG_TamingOrb"; val.m_enabled = true; val.m_amount = 1; val.m_item = component; val.m_resources = BuildRequirements(objectDb, FeedLikeGrandmaPlugin.TamingOrbRecipe.Value).ToArray(); if (TryResolveCraftingStation(FeedLikeGrandmaPlugin.TamingOrbCraftingStation.Value, out CraftingStation station, out int stationLevel)) { val.m_craftingStation = station; val.m_minStationLevel = stationLevel; } objectDb.m_recipes.Add(val); } } private static IEnumerable BuildRequirements(ObjectDB objectDb, string recipeText) { if (string.IsNullOrWhiteSpace(recipeText)) { yield break; } string[] array = recipeText.Split(new char[1] { ',' }, StringSplitOptions.RemoveEmptyEntries); string[] array2 = array; for (int i = 0; i < array2.Length; i++) { string[] array3 = array2[i].Trim().Split(new char[1] { ':' }, StringSplitOptions.RemoveEmptyEntries); if (array3.Length != 0) { string text = array3[0].Trim(); int result = 1; if (array3.Length > 1) { int.TryParse(array3[1].Trim(), NumberStyles.Integer, CultureInfo.InvariantCulture, out result); result = Mathf.Max(1, result); } GameObject val = FindItemPrefab(objectDb, text); ItemDrop val2 = (((Object)(object)val == (Object)null) ? null : val.GetComponent()); if (val2 == null) { FeedLikeGrandmaPlugin.Log.LogWarning((object)("FLG_TamingOrb recipe ingredient '" + text + "' was not found.")); continue; } yield return new Requirement { m_resItem = val2, m_amount = result, m_amountPerLevel = 0, m_recover = true }; } } } private static bool TryResolveCraftingStation(string stationText, out CraftingStation? station, out int stationLevel) { station = null; stationLevel = 1; if (string.IsNullOrWhiteSpace(stationText) || stationText.Equals("None", StringComparison.OrdinalIgnoreCase)) { return false; } string[] array = stationText.Split(new char[1] { ':' }, StringSplitOptions.RemoveEmptyEntries); string text = array[0].Trim(); if (array.Length > 1) { int.TryParse(array[1].Trim(), NumberStyles.Integer, CultureInfo.InvariantCulture, out stationLevel); stationLevel = Mathf.Max(1, stationLevel); } GameObject val = (((Object)(object)ZNetScene.instance == (Object)null) ? null : ZNetScene.instance.GetPrefab(text)); station = (((Object)(object)val == (Object)null) ? null : val.GetComponent()); if (station == null) { if (IsZNetScenePrefabRegistryReady()) { FeedLikeGrandmaPlugin.Log.LogWarning((object)("FLG_TamingOrb crafting station '" + text + "' was not found. Recipe will not require a station.")); } return false; } return true; } private static bool IsZNetScenePrefabRegistryReady() { if ((Object)(object)ZNetScene.instance != (Object)null && ZNetScene.instance.m_prefabs != null && ZNetScene.instance.m_prefabs.Count > 0 && ZNetScene.instance.m_namedPrefabs != null) { return ZNetScene.instance.m_namedPrefabs.Count > 0; } return false; } private static bool IsTamingOrb(ItemData item) { if (item == null || item.m_shared == null) { return false; } if (item.m_dropPrefab != null && ((Object)item.m_dropPrefab).name == "FLG_TamingOrb") { if (_tamingOrbSharedData == null) { _tamingOrbSharedData = item.m_shared; } NonTamingOrbSharedData.Remove(item.m_shared); return true; } if (item.m_shared == _tamingOrbSharedData || item.m_shared.m_name == "$flg_taming_orb") { if (_tamingOrbSharedData == null) { _tamingOrbSharedData = item.m_shared; } NonTamingOrbSharedData.Remove(item.m_shared); return true; } if (NonTamingOrbSharedData.Contains(item.m_shared)) { return false; } GameObject val = default(GameObject); if (ObjectDB.instance != null && ObjectDB.instance.TryGetItemPrefab(item.m_shared, ref val)) { if (val != null && ((Object)val).name == "FLG_TamingOrb") { _tamingOrbSharedData = item.m_shared; NonTamingOrbSharedData.Remove(item.m_shared); return true; } NonTamingOrbSharedData.Add(item.m_shared); return false; } return false; } private static GameObject? FindSpawnableItemPrefab(ObjectDB objectDb, string prefabName) { if (ZNetScene.instance != null && ZNetScene.instance.m_namedPrefabs.TryGetValue(StringExtensionMethods.GetStableHashCode(prefabName), out var value) && value != null && HasSpawnableItemShape(value)) { return value; } GameObject val = FindItemPrefab(objectDb, prefabName); if (val != null && HasSpawnableItemShape(val)) { return val; } return null; } private static GameObject? FindItemPrefab(ObjectDB? objectDb, string prefabName) { if (objectDb != null) { int stableHashCode = StringExtensionMethods.GetStableHashCode(prefabName); if (objectDb.m_itemByHash.TryGetValue(stableHashCode, out var value) && value != null) { return value; } GameObject val = ((IEnumerable)objectDb.m_items).FirstOrDefault((Func)((GameObject item) => item != null && ((Object)item).name == prefabName)); if (val != null) { return val; } GameObject itemPrefab = objectDb.GetItemPrefab(prefabName); if (itemPrefab != null) { return itemPrefab; } } if (ZNetScene.instance != null && ZNetScene.instance.m_namedPrefabs.TryGetValue(StringExtensionMethods.GetStableHashCode(prefabName), out var value2) && value2 != null) { return value2; } return null; } private static bool HasSpawnableItemShape(GameObject prefab) { if (prefab == null) { return false; } if (prefab.GetComponent() != null && prefab.GetComponent() != null && prefab.GetComponent() != null && prefab.GetComponent() != null && prefab.GetComponent() != null) { return prefab.GetComponentInChildren(true) != null; } return false; } private static Material? CreateSafeVisualMaterial(Material? preferred, Material? fallback) { //IL_0004: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Expected O, but got Unknown //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Expected O, but got Unknown try { if (preferred != null) { return new Material(preferred); } if (fallback != null) { return new Material(fallback); } } catch (Exception ex) { FeedLikeGrandmaPlugin.Log.LogDebug((object)("Could not clone FLG_TamingOrb material: " + ex.Message)); } return null; } private static void TintDragonTearCoreMeshes(GameObject prefab, Color color) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) if ((0u | (TintRendererAtPath(prefab, "attach/model/inner", color, forceOpaque: true) ? 1u : 0u) | (TintRendererAtPath(prefab, "attach/model/hull", color, forceOpaque: false) ? 1u : 0u)) == 0) { FeedLikeGrandmaPlugin.Log.LogDebug((object)"FLG_TamingOrb: DragonTear core mesh renderers were not found."); } } private static bool TintRendererAtPath(GameObject prefab, string path, Color color, bool forceOpaque) { //IL_0041: Unknown result type (might be due to invalid IL or missing references) Transform val = prefab.transform.Find(path); Renderer val2 = (((Object)(object)val == (Object)null) ? null : ((Component)val).GetComponent()); if (val2 == null) { return false; } Material[] sharedMaterials = val2.sharedMaterials; for (int i = 0; i < sharedMaterials.Length; i++) { Material val3 = CreateSafeVisualMaterial(sharedMaterials[i], null); if (val3 != null) { TintMaterial(val3, color, forceOpaque); sharedMaterials[i] = val3; } } val2.sharedMaterials = sharedMaterials; val2.enabled = true; return true; } private static void TintMaterial(Material material, Color color, bool forceOpaque) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: 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_00ba: Unknown result type (might be due to invalid IL or missing references) Color val = (material.HasProperty(ColorProperty) ? material.GetColor(ColorProperty) : Color.white); Color val2 = default(Color); ((Color)(ref val2))..ctor(color.r, color.g, color.b, forceOpaque ? 1f : val.a); if (material.HasProperty(ColorProperty)) { material.SetColor(ColorProperty, val2); } if (material.HasProperty(TintColorProperty)) { material.SetColor(TintColorProperty, val2); } if (material.HasProperty(EmissionColorProperty)) { material.EnableKeyword("_EMISSION"); material.SetColor(EmissionColorProperty, new Color(color.r, color.g, color.b, 1f) * 0.75f); } if (forceOpaque) { if (material.HasProperty(ModeProperty)) { material.SetFloat(ModeProperty, 0f); } if (material.HasProperty(SrcBlendProperty)) { material.SetFloat(SrcBlendProperty, 1f); } if (material.HasProperty(DstBlendProperty)) { material.SetFloat(DstBlendProperty, 0f); } if (material.HasProperty(ZWriteProperty)) { material.SetFloat(ZWriteProperty, 1f); } material.DisableKeyword("_ALPHABLEND_ON"); material.DisableKeyword("_ALPHAPREMULTIPLY_ON"); material.renderQueue = -1; } } private static bool TryGetPrefabName(GameObject gameObject, out string prefabName) { prefabName = string.Empty; if (gameObject == null) { return false; } prefabName = ((Object)gameObject).name; int num = prefabName.IndexOf('('); int num2 = prefabName.IndexOf(' '); int num3 = ((num < 0) ? num2 : ((num2 < 0) ? num : Mathf.Min(num, num2))); if (num3 >= 0) { prefabName = prefabName.Substring(0, num3); } if (!string.IsNullOrWhiteSpace(prefabName)) { return (Object)(object)ZNetScene.instance.GetPrefab(prefabName) != (Object)null; } return false; } private static Sprite? CreateCreatureIcon(GameObject creaturePrefab) { //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) GameObject val = null; try { val = CreateCreatureIconClone(creaturePrefab); if (val == null) { return null; } if (!TryGetRenderBounds(val, out var bounds)) { return null; } Transform transform = val.transform; transform.position -= ((Bounds)(ref bounds)).center; if (!TryGetRenderBounds(val, out bounds)) { return null; } EnsureIconRenderer(); if ((Object)(object)_iconCamera == (Object)null) { return null; } return RenderCreatureIcon(val, bounds); } catch (Exception ex) { FeedLikeGrandmaPlugin.Log.LogDebug((object)("Could not render FLG_TamingOrb creature icon for " + ((Object)creaturePrefab).name + ": " + ex.Message)); return null; } finally { if (val != null) { RemoveIconCloneCharacters(val); Object.DestroyImmediate((Object)(object)val); } } } private static GameObject? CreateCreatureIconClone(GameObject creaturePrefab) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Expected O, but got Unknown //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject("FeedLikeGrandma Creature Icon Root"); val.SetActive(false); try { GameObject val2 = Object.Instantiate(creaturePrefab, val.transform); ((Object)val2).name = ((Object)creaturePrefab).name + "_FLGIcon"; if (!StripForIconRender(val.transform)) { return null; } SetLayerRecursive(val2, 31); val2.transform.SetParent((Transform)null, false); val2.transform.position = Vector3.zero; val2.transform.rotation = CreatureIconRotation; val2.SetActive(true); return val2; } finally { Object.DestroyImmediate((Object)(object)val); } } private static bool StripForIconRender(Transform parent) { for (int i = 0; i < parent.childCount; i++) { if (!StripForIconRender(parent.GetChild(i))) { return false; } } List iconComponentDeletionOrder = GetIconComponentDeletionOrder(parent); if (iconComponentDeletionOrder == null) { return false; } GameObject gameObject = ((Component)parent).gameObject; foreach (Type item in iconComponentDeletionOrder) { Component[] components = gameObject.GetComponents(item); foreach (Component val in components) { if (val != null && !IsIconRenderComponent(val)) { try { Object.DestroyImmediate((Object)(object)val); } catch (Exception ex) { FeedLikeGrandmaPlugin.Log.LogDebug((object)("Could not strip " + ((object)val).GetType().Name + " while rendering FLG_TamingOrb creature icon: " + ex.Message)); return false; } } } } return true; } private static bool IsIconRenderComponent(Component component) { if (!(component is Transform) && !(component is MeshRenderer) && !(component is SkinnedMeshRenderer)) { return component is MeshFilter; } return true; } private static List? GetIconComponentDeletionOrder(Transform transform) { List list = new List(); HashSet visited = new HashSet(); HashSet recursionStack = new HashSet(); Component[] components = ((Component)transform).gameObject.GetComponents(); foreach (Component val in components) { if (val != null && !AddIconComponentDeletionOrder(((object)val).GetType(), visited, recursionStack, list)) { return null; } } list.Reverse(); return list; } private static bool AddIconComponentDeletionOrder(Type type, HashSet visited, HashSet recursionStack, List result) { if (visited.Contains(type)) { return true; } List list = null; int num = result.FindIndex(type.IsSubclassOf); if (num >= 0) { list = result.Skip(num).ToList(); result.RemoveRange(num, result.Count - num); } if (recursionStack.Contains(type)) { return false; } recursionStack.Add(type); foreach (Type iconComponentDependency in GetIconComponentDependencies(type)) { if (!AddIconComponentDeletionOrder(iconComponentDependency, visited, recursionStack, result)) { return false; } } if (list != null) { result.AddRange(list); } else { result.Add(type); } recursionStack.Remove(type); visited.Add(type); return true; } private static List GetIconComponentDependencies(Type type) { if (IconComponentDependencies.TryGetValue(type, out List value)) { return value; } value = new List(); foreach (RequireComponent customAttribute in ((MemberInfo)type).GetCustomAttributes(inherit: true)) { if (customAttribute.m_Type0 != null) { value.Add(customAttribute.m_Type0); } if (customAttribute.m_Type1 != null) { value.Add(customAttribute.m_Type1); } if (customAttribute.m_Type2 != null) { value.Add(customAttribute.m_Type2); } } value = value.Distinct().ToList(); IconComponentDependencies[type] = value; return value; } private static void RemoveIconCloneCharacters(GameObject clone) { Character[] componentsInChildren = clone.GetComponentsInChildren(true); foreach (Character val in componentsInChildren) { if (!((Object)(object)val == (Object)null)) { Character.GetAllCharacters().Remove(val); Character.Instances.Remove((IMonoUpdater)(object)val); EnemyHud instance = EnemyHud.instance; if (instance != null) { instance.RemoveCharacterHud(val); } } } } private static void SetLayerRecursive(GameObject gameObject, int layer) { //IL_001b: Unknown result type (might be due to invalid IL or missing references) gameObject.layer = layer; foreach (Transform item in gameObject.transform) { SetLayerRecursive(((Component)item).gameObject, layer); } } private static bool TryGetRenderBounds(GameObject gameObject, out Bounds bounds) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) bounds = default(Bounds); bool flag = false; Renderer[] componentsInChildren = gameObject.GetComponentsInChildren(false); foreach (Renderer val in componentsInChildren) { if (val != null && (val is MeshRenderer || val is SkinnedMeshRenderer)) { if (!flag) { bounds = val.bounds; flag = true; } else { ((Bounds)(ref bounds)).Encapsulate(val.bounds); } } } return flag; } private static void EnsureIconRenderer() { //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Expected O, but got Unknown //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: Expected O, but got Unknown if (!((Object)(object)_iconCamera != (Object)null)) { int cullingMask = int.MinValue; GameObject val = new GameObject("FeedLikeGrandma Creature Icon Camera", new Type[1] { typeof(Camera) }); Object.DontDestroyOnLoad((Object)val); _iconCamera = val.GetComponent(); ((Behaviour)_iconCamera).enabled = false; _iconCamera.backgroundColor = Color.clear; _iconCamera.clearFlags = (CameraClearFlags)2; _iconCamera.cullingMask = cullingMask; _iconCamera.orthographic = false; _iconCamera.fieldOfView = 0.5f; _iconCamera.nearClipPlane = 0.01f; _iconCamera.farClipPlane = 100000f; GameObject val2 = new GameObject("FeedLikeGrandma Creature Icon Light", new Type[1] { typeof(Light) }); Object.DontDestroyOnLoad((Object)val2); _iconLight = val2.GetComponent(); _iconLight.type = (LightType)1; _iconLight.intensity = 1.35f; _iconLight.cullingMask = cullingMask; ((Behaviour)_iconLight).enabled = false; } } private static Sprite? RenderCreatureIcon(GameObject clone, Bounds bounds) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_00af: 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_010d: Unknown result type (might be due to invalid IL or missing references) //IL_0186: Unknown result type (might be due to invalid IL or missing references) //IL_01a6: Unknown result type (might be due to invalid IL or missing references) //IL_01ab: Unknown result type (might be due to invalid IL or missing references) //IL_01b2: Unknown result type (might be due to invalid IL or missing references) //IL_01b9: Unknown result type (might be due to invalid IL or missing references) //IL_01d4: Unknown result type (might be due to invalid IL or missing references) //IL_01e9: Unknown result type (might be due to invalid IL or missing references) //IL_01f5: Unknown result type (might be due to invalid IL or missing references) //IL_020f: Unknown result type (might be due to invalid IL or missing references) //IL_021e: Unknown result type (might be due to invalid IL or missing references) //IL_0228: Expected O, but got Unknown if ((Object)(object)_iconCamera == (Object)null) { return null; } float num = Mathf.Max(((Bounds)(ref bounds)).size.x, ((Bounds)(ref bounds)).size.y) + 0.25f; float num2 = Mathf.Max(8f, num / Mathf.Tan(_iconCamera.fieldOfView * ((float)Math.PI / 180f)) * 1.2f); ((Component)_iconCamera).transform.position = new Vector3(0f, 0f, num2); ((Component)_iconCamera).transform.rotation = Quaternion.Euler(0f, 180f, 0f); Camera? iconCamera = _iconCamera; Vector3 size = ((Bounds)(ref bounds)).size; iconCamera.farClipPlane = num2 + Mathf.Max(1000f, ((Vector3)(ref size)).magnitude * 8f); if ((Object)(object)_iconLight != (Object)null) { ((Component)_iconLight).transform.position = ((Component)_iconCamera).transform.position; ((Component)_iconLight).transform.rotation = Quaternion.Euler(5f, 180f, 5f); } RenderTexture active = RenderTexture.active; RenderTexture targetTexture = _iconCamera.targetTexture; bool enabled = (Object)(object)_iconLight != (Object)null && ((Behaviour)_iconLight).enabled; RenderTexture temporary = RenderTexture.GetTemporary(128, 128, 24, (RenderTextureFormat)0, (RenderTextureReadWrite)2); try { if ((Object)(object)_iconLight != (Object)null) { ((Behaviour)_iconLight).enabled = true; } _iconCamera.targetTexture = temporary; RenderTexture.active = temporary; GL.Clear(true, true, Color.clear); _iconCamera.Render(); Texture2D val = new Texture2D(128, 128, (TextureFormat)4, false) { filterMode = (FilterMode)1, wrapMode = (TextureWrapMode)1, name = "FLG_" + ((Object)clone).name + "_Icon" }; val.ReadPixels(new Rect(0f, 0f, 128f, 128f), 0, 0); val.Apply(); return Sprite.Create(val, new Rect(0f, 0f, 128f, 128f), new Vector2(0.5f, 0.5f)); } finally { if ((Object)(object)_iconLight != (Object)null) { ((Behaviour)_iconLight).enabled = enabled; } _iconCamera.targetTexture = targetTexture; RenderTexture.active = active; RenderTexture.ReleaseTemporary(temporary); } } private static Sprite? CreateTintedIcon(Sprite? source, Color tint) { //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_002c: 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_00d7: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Unknown result type (might be due to invalid IL or missing references) if (source == null || source.texture == null) { return source; } Texture2D val = DuplicateSpriteTexture(source); if (val == null) { return source; } Color[] pixels = val.GetPixels(); for (int i = 0; i < pixels.Length; i++) { Color val2 = pixels[i]; if (val2.a > 0.01f) { pixels[i] = new Color(Mathf.Lerp(val2.r, tint.r, 0.45f), Mathf.Lerp(val2.g, tint.g, 0.45f), Mathf.Lerp(val2.b, tint.b, 0.45f), val2.a); } } val.SetPixels(pixels); val.Apply(); ((Object)val).name = "FLG_TamingOrb_Icon"; return Sprite.Create(val, new Rect(0f, 0f, (float)((Texture)val).width, (float)((Texture)val).height), new Vector2(0.5f, 0.5f), source.pixelsPerUnit); } private static Sprite? CreateCompositeTamingOrbIcon(Sprite? creatureIcon) { //IL_00fe: Unknown result type (might be due to invalid IL or missing references) //IL_010d: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Unknown result type (might be due to invalid IL or missing references) if (creatureIcon == null || creatureIcon.texture == null) { return null; } Texture2D val = CreateReadableIconTexture(_icon, 128, 128); int num = Mathf.Clamp(Mathf.RoundToInt(99.84f), 1, 128); Texture2D val2 = CreateReadableIconTexture(creatureIcon, num, num); Color32[] pixels = val.GetPixels32(); Color32[] pixels2 = val2.GetPixels32(); ClearIconTextureBorder(pixels2, num, num, 2); int num2 = (128 - num) / 2; for (int i = 0; i < num; i++) { int num3 = i + num2; for (int j = 0; j < num; j++) { int num4 = j + num2; int num5 = num3 * 128 + num4; int num6 = i * num + j; pixels[num5] = AlphaBlend(pixels[num5], pixels2[num6]); } } val.SetPixels32(pixels); val.Apply(); ((Object)val).name = "FLG_TamingOrb_Creature_Icon"; Object.Destroy((Object)(object)val2); return Sprite.Create(val, new Rect(0f, 0f, (float)((Texture)val).width, (float)((Texture)val).height), new Vector2(0.5f, 0.5f)); } private static void ClearIconTextureBorder(Color32[] pixels, int width, int height, int thickness) { //IL_0057: 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_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) if (pixels.Length != width * height || width <= 0 || height <= 0 || thickness <= 0) { return; } thickness = Mathf.Min(new int[3] { thickness, width / 2, height / 2 }); Color32 val = default(Color32); ((Color32)(ref val))..ctor((byte)0, (byte)0, (byte)0, (byte)0); for (int i = 0; i < thickness; i++) { int num = i * width; int num2 = (height - 1 - i) * width; for (int j = 0; j < width; j++) { pixels[num + j] = val; pixels[num2 + j] = val; } for (int k = 0; k < height; k++) { pixels[k * width + i] = val; pixels[k * width + (width - 1 - i)] = val; } } } private static Texture2D CreateReadableIconTexture(Sprite? source, int width, int height) { //IL_0004: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Expected O, but got Unknown //IL_005e: Unknown result type (might be due to invalid IL or missing references) Texture2D val = new Texture2D(width, height, (TextureFormat)4, false) { filterMode = (FilterMode)1, wrapMode = (TextureWrapMode)1 }; if (source == null || source.texture == null) { return val; } Texture2D val2 = DuplicateSpriteTexture(source); if (val2 == null) { return val; } RenderTexture active = RenderTexture.active; RenderTexture temporary = RenderTexture.GetTemporary(width, height, 0, (RenderTextureFormat)0, (RenderTextureReadWrite)2); try { Graphics.Blit((Texture)(object)val2, temporary); RenderTexture.active = temporary; val.ReadPixels(new Rect(0f, 0f, (float)width, (float)height), 0, 0); val.Apply(); return val; } finally { RenderTexture.active = active; RenderTexture.ReleaseTemporary(temporary); Object.Destroy((Object)(object)val2); } } private static Color32 AlphaBlend(Color32 destination, Color32 source) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: 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_0104: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) float num = (float)(int)source.a / 255f; if (num <= 0f) { return destination; } if (num >= 1f) { return source; } float num2 = (float)(int)destination.a / 255f; float num3 = num + num2 * (1f - num); if (num3 <= 0f) { return new Color32((byte)0, (byte)0, (byte)0, (byte)0); } byte num4 = (byte)Mathf.RoundToInt(((float)(int)source.r / 255f * num + (float)(int)destination.r / 255f * num2 * (1f - num)) / num3 * 255f); byte b = (byte)Mathf.RoundToInt(((float)(int)source.g / 255f * num + (float)(int)destination.g / 255f * num2 * (1f - num)) / num3 * 255f); byte b2 = (byte)Mathf.RoundToInt(((float)(int)source.b / 255f * num + (float)(int)destination.b / 255f * num2 * (1f - num)) / num3 * 255f); byte b3 = (byte)Mathf.RoundToInt(num3 * 255f); return new Color32(num4, b, b2, b3); } private static Texture2D? DuplicateSpriteTexture(Sprite source) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Expected O, but got Unknown Texture texture = (Texture)(object)source.texture; Rect textureRect = source.textureRect; int num = Mathf.Max(1, Mathf.RoundToInt(((Rect)(ref textureRect)).width)); int num2 = Mathf.Max(1, Mathf.RoundToInt(((Rect)(ref textureRect)).height)); RenderTexture active = RenderTexture.active; RenderTexture temporary = RenderTexture.GetTemporary(texture.width, texture.height, 0, (RenderTextureFormat)7, (RenderTextureReadWrite)2); try { Graphics.Blit(texture, temporary); RenderTexture.active = temporary; Texture2D val = new Texture2D(num, num2, (TextureFormat)4, false) { filterMode = (FilterMode)1, wrapMode = (TextureWrapMode)1 }; val.ReadPixels(textureRect, 0, 0); val.Apply(); return val; } catch (Exception ex) { FeedLikeGrandmaPlugin.Log.LogDebug((object)("Could not duplicate FLG_TamingOrb source icon: " + ex.Message)); return null; } finally { RenderTexture.active = active; RenderTexture.ReleaseTemporary(temporary); } } } } namespace ServerSync { [PublicAPI] internal abstract class OwnConfigEntryBase { public object? LocalBaseValue; public bool SynchronizedConfig = true; public abstract ConfigEntryBase BaseConfig { get; } } [PublicAPI] internal class SyncedConfigEntry(ConfigEntry sourceConfig) : OwnConfigEntryBase() { public readonly ConfigEntry SourceConfig = sourceConfig; public override ConfigEntryBase BaseConfig => (ConfigEntryBase)(object)SourceConfig; public T Value { get { return SourceConfig.Value; } set { SourceConfig.Value = value; } } 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 : 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(configSync2.Name + " ConfigSync", (Action)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 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 CurrentList = new List(adminList.GetList()); while (true) { yield return (object)new WaitForSeconds(30f); if (!adminList.GetList().SequenceEqual(CurrentList)) { CurrentList = new List(adminList.GetList()); List 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 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(configSync.Name + " ConfigSync", (Action)configSync.RPC_FromServerConfigSync); } } } private class ParsedConfigs { public readonly Dictionary configValues = new Dictionary(); public readonly Dictionary customValues = new Dictionary(); } [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 Package = new List(); 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? __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(); } __state[Assembly.GetExecutingAssembly()] = bufferingSocket; } [HarmonyPostfix] private static void Postfix(Dictionary __state, ZNet __instance, ZRpc rpc) { ZNetPeer peer; if (__instance.IsServer()) { object obj = AccessTools.DeclaredMethod(typeof(ZNet), "GetPeer", new Type[1] { typeof(ZRpc) }, (Type[])null).Invoke(__instance, new object[1] { rpc }); peer = (ZNetPeer)((obj is ZNetPeer) ? obj : null); if (peer == null) { SendBufferedData(); } else { ((MonoBehaviour)__instance).StartCoroutine(sendAsync()); } } void SendBufferedData() { if (rpc.GetSocket() is BufferingSocket bufferingSocket) { AccessTools.DeclaredField(typeof(ZRpc), "m_socket").SetValue(rpc, bufferingSocket.Original); object? obj2 = AccessTools.DeclaredMethod(typeof(ZNet), "GetPeer", new Type[1] { typeof(ZRpc) }, (Type[])null).Invoke(__instance, new object[1] { rpc }); ZNetPeer val = (ZNetPeer)((obj2 is ZNetPeer) ? obj2 : null); if (val != null) { AccessTools.DeclaredField(typeof(ZNetPeer), "m_socket").SetValue(val, bufferingSocket.Original); } } BufferingSocket bufferingSocket2 = __state[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 entries = new List(); 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(rpc.GetSocket().GetHostName())) : listContainsId.Invoke(ZNet.instance, new object[2] { adminList, rpc.GetSocket().GetHostName() })) }); ZPackage package = ConfigsToPackage(configSync.allConfigs.Select((OwnConfigEntryBase c) => c.BaseConfig), configSync.allCustomValues, entries, partial: false); yield return ((MonoBehaviour)__instance).StartCoroutine(configSync.sendZPackage(new List { 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 configSyncs; private readonly HashSet allConfigs = new HashSet(); private HashSet allCustomValues = new HashSet(); 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> configValueCache = new Dictionary>(); private readonly List> cacheExpirations = new List>(); 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 == true; } if (!num) { goto IL_0052; } int result = ((!lockExempt) ? 1 : 0); goto IL_0053; IL_0052: result = 0; goto IL_0053; IL_0053: return (byte)result != 0; } 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? SourceOfTruthChanged; private event Action? lockedConfigChanged; static ConfigSync() { ProcessingServerUpdate = false; configSyncs = new HashSet(); lockExempt = false; packageCounter = 0L; RuntimeHelpers.RunClassConstructor(typeof(VersionCheck).TypeHandle); } public ConfigSync(string name) { Name = name; configSyncs.Add(this); new VersionCheck(this); } public SyncedConfigEntry AddConfigEntry(ConfigEntry configEntry) { OwnConfigEntryBase ownConfigEntryBase = configData((ConfigEntryBase)(object)configEntry); SyncedConfigEntry syncedEntry = ownConfigEntryBase as SyncedConfigEntry; if (syncedEntry == null) { syncedEntry = new SyncedConfigEntry(configEntry); AccessTools.DeclaredField(typeof(ConfigDescription), "k__BackingField").SetValue(((ConfigEntryBase)configEntry).Description, new object[1] { new ConfigurationManagerAttributes() }.Concat(((ConfigEntryBase)configEntry).Description.Tags ?? Array.Empty()).Concat(new SyncedConfigEntry[1] { syncedEntry }).ToArray()); configEntry.SettingChanged += delegate { if (!ProcessingServerUpdate && syncedEntry.SynchronizedConfig) { Broadcast(ZRoutedRpc.Everybody, (ConfigEntryBase)configEntry); } }; allConfigs.Add(syncedEntry); } return syncedEntry; } public SyncedConfigEntry AddLockingConfigEntry(ConfigEntry lockingConfig) where T : IConvertible { if (lockedConfig != null) { throw new Exception("Cannot initialize locking ConfigEntry twice"); } lockedConfig = AddConfigEntry(lockingConfig); lockingConfig.SettingChanged += delegate { this.lockedConfigChanged?.Invoke(); }; return (SyncedConfigEntry)lockedConfig; } internal void AddCustomValue(CustomSyncedValueBase customValue) { if (allCustomValues.Select((CustomSyncedValueBase v) => v.Identifier).Concat(new string[1] { "serverversion" }).Contains(customValue.Identifier)) { throw new Exception("Cannot have multiple settings with the same name or with a reserved name (serverversion)"); } allCustomValues.Add(customValue); allCustomValues = new HashSet(allCustomValues.OrderByDescending((CustomSyncedValueBase v) => v.Priority)); customValue.ValueChanged += delegate { if (!ProcessingServerUpdate) { Broadcast(ZRoutedRpc.Everybody, customValue); } }; } 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 kv) { if (kv.Key < DateTimeOffset.Now.Ticks) { configValueCache.Remove(kv.Value); return true; } return false; }); byte b = package.ReadByte(); if ((b & 2) != 0) { long num = package.ReadLong(); string text2 = sender.ToString() + num; if (!configValueCache.TryGetValue(text2, out SortedDictionary value)) { value = new SortedDictionary(); configValueCache[text2] = value; cacheExpirations.Add(new KeyValuePair(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 & 4) != 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 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 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 dictionary = allConfigs.Where((OwnConfigEntryBase c) => c.SynchronizedConfig).ToDictionary((OwnConfigEntryBase c) => c.BaseConfig.Definition.Section + "_" + c.BaseConfig.Definition.Key, (OwnConfigEntryBase c) => c); Dictionary dictionary2 = allCustomValues.ToDictionary((CustomSyncedValueBase c) => c.Identifier, (CustomSyncedValueBase c) => c); int num = package.ReadInt(); for (int num2 = 0; num2 < num; num2++) { 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) { ConfigSync configSync = configSyncs.FirstOrDefault((ConfigSync cs) => cs.allConfigs.Contains(config)); if (configSync == null) { return true; } return configSync.IsSourceOfTruth || !config.SynchronizedConfig || config.LocalBaseValue == null || (!configSync.IsLocked && (config != configSync.lockedConfig || lockExempt)); } private void serverLockedSettingChanged() { foreach (OwnConfigEntryBase allConfig in allConfigs) { configAttribute(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 distributeConfigToPeers(ZNetPeer peer, ZPackage package) { 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 (peer.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) { peer.m_rpc.Invoke(text, new object[1] { pkg }); } else { rpc.InvokeRoutedRPC(peer.m_server ? 0 : peer.m_uid, text, new object[1] { pkg }); } } IEnumerable waitForQueue() { float timeout = Time.time + 30f; while (peer.m_socket.GetSendQueueSize() > 20000) { if (Time.time > timeout) { Debug.Log((object)$"Disconnecting {peer.m_uid} after 30 seconds config sending timeout"); peer.m_rpc.Invoke("Error", new object[1] { (object)(ConnectionStatus)5 }); ZNet.instance.Disconnect(peer); break; } yield return false; } } } private IEnumerator sendZPackage(long target, ZPackage package) { if (!Object.op_Implicit((Object)(object)ZNet.instance)) { return Enumerable.Empty().GetEnumerator(); } List list = (List)AccessTools.DeclaredField(typeof(ZRoutedRpc), "m_peers").GetValue(ZRoutedRpc.instance); if (target != ZRoutedRpc.Everybody) { list = list.Where((ZNetPeer p) => p.m_uid == target).ToList(); } return sendZPackage(list, package); } private IEnumerator sendZPackage(List peers, ZPackage package) { if (!Object.op_Implicit((Object)(object)ZNet.instance)) { yield break; } byte[] rawData = package.GetArray(); if (rawData != null && rawData.LongLength > 10000) { ZPackage compressedPackage = new ZPackage(); compressedPackage.Write((byte)4); MemoryStream output = new MemoryStream(); using (DeflateStream deflateStream = new DeflateStream(output, CompressionLevel.Optimal)) { deflateStream.Write(rawData, 0, rawData.Length); } compressedPackage.Write(output.ToArray()); package = compressedPackage; } List> writers = (from p in peers where p.IsReady() select distributeConfigToPeers(p, package)).ToList(); writers.RemoveAll((IEnumerator writer) => !writer.MoveNext()); while (writers.Count > 0) { yield return null; writers.RemoveAll((IEnumerator writer) => !writer.MoveNext()); } } private void Broadcast(long target, params ConfigEntryBase[] configs) { if (!IsLocked || isServer) { ZPackage package = ConfigsToPackage(configs); ZNet instance = ZNet.instance; if (instance != null) { ((MonoBehaviour)instance).StartCoroutine(sendZPackage(target, package)); } } } private void Broadcast(long target, params CustomSyncedValueBase[] customValues) { if (!IsLocked || isServer) { ZPackage package = ConfigsToPackage(null, customValues); ZNet instance = ZNet.instance; if (instance != null) { ((MonoBehaviour)instance).StartCoroutine(sendZPackage(target, package)); } } } private static OwnConfigEntryBase? configData(ConfigEntryBase config) { return config.Description.Tags?.OfType().SingleOrDefault(); } public static SyncedConfigEntry? ConfigData(ConfigEntry config) { return ((ConfigEntryBase)config).Description.Tags?.OfType>().SingleOrDefault(); } private static T configAttribute(ConfigEntryBase config) { return config.Description.Tags.OfType().First(); } private static Type configType(ConfigEntryBase config) { return configType(config.SettingType); } private static Type configType(Type type) { return type.IsEnum ? Enum.GetUnderlyingType(type) : type; } private static ZPackage ConfigsToPackage(IEnumerable? configs = null, IEnumerable? customValues = null, IEnumerable? packageEntries = null, bool partial = true) { //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Expected O, but got Unknown List list = configs?.Where((ConfigEntryBase config) => configData(config).SynchronizedConfig).ToList() ?? new List(); List list2 = customValues?.ToList() ?? new List(); ZPackage val = new ZPackage(); val.Write((byte)(partial ? 1 : 0)); val.Write(list.Count + list2.Count + (packageEntries?.Count() ?? 0)); foreach (PackageEntry item in packageEntries ?? Array.Empty()) { AddEntryToPackage(val, item); } foreach (CustomSyncedValueBase item2 in list2) { AddEntryToPackage(val, new PackageEntry { section = "Internal", key = item2.Identifier, type = item2.Type, value = item2.BoxedValue }); } foreach (ConfigEntryBase item3 in list) { AddEntryToPackage(val, new PackageEntry { section = item3.Definition.Section, key = item3.Definition.Key, type = configType(item3), value = item3.BoxedValue }); } return val; } private static void AddEntryToPackage(ZPackage package, PackageEntry entry) { package.Write(entry.section); package.Write(entry.key); package.Write((entry.value == null) ? "" : GetZPackageTypeString(entry.type)); AddValueToZPackage(package, entry.value); } private static string GetZPackageTypeString(Type type) { return type.AssemblyQualifiedName; } private static void AddValueToZPackage(ZPackage package, object? value) { Type type = value?.GetType(); if (value is Enum) { value = ((IConvertible)value).ToType(Enum.GetUnderlyingType(value.GetType()), CultureInfo.InvariantCulture); } else { if (value is ICollection collection) { package.Write(collection.Count); { foreach (object item in collection) { AddValueToZPackage(package, item); } return; } } if ((object)type != null && type.IsValueType && !type.IsPrimitive) { FieldInfo[] fields = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); package.Write(fields.Length); FieldInfo[] array = fields; foreach (FieldInfo fieldInfo in array) { package.Write(GetZPackageTypeString(fieldInfo.FieldType)); AddValueToZPackage(package, fieldInfo.GetValue(value)); } return; } } ZRpc.Serialize(new object[1] { value }, ref package); } private static object ReadValueWithTypeFromZPackage(ZPackage package, Type type) { if ((object)type != null && type.IsValueType && !type.IsPrimitive && !type.IsEnum) { FieldInfo[] fields = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); int num = package.ReadInt(); if (num != fields.Length) { throw new InvalidDeserializationTypeException { received = $"(field count: {num})", expected = $"(field count: {fields.Length})" }; } object uninitializedObject = FormatterServices.GetUninitializedObject(type); FieldInfo[] array = fields; foreach (FieldInfo fieldInfo in array) { string text = package.ReadString(); if (text != GetZPackageTypeString(fieldInfo.FieldType)) { throw new InvalidDeserializationTypeException { received = text, expected = GetZPackageTypeString(fieldInfo.FieldType), field = fieldInfo.Name }; } fieldInfo.SetValue(uninitializedObject, ReadValueWithTypeFromZPackage(package, fieldInfo.FieldType)); } return uninitializedObject; } if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Dictionary<, >)) { int num2 = package.ReadInt(); IDictionary dictionary = (IDictionary)Activator.CreateInstance(type); Type type2 = typeof(KeyValuePair<, >).MakeGenericType(type.GenericTypeArguments); FieldInfo field = type2.GetField("key", BindingFlags.Instance | BindingFlags.NonPublic); FieldInfo field2 = type2.GetField("value", BindingFlags.Instance | BindingFlags.NonPublic); for (int j = 0; j < num2; j++) { object obj = ReadValueWithTypeFromZPackage(package, type2); dictionary.Add(field.GetValue(obj), field2.GetValue(obj)); } return dictionary; } if (type != typeof(List) && type.IsGenericType) { Type type3 = typeof(ICollection<>).MakeGenericType(type.GenericTypeArguments[0]); if ((object)type3 != null && type3.IsAssignableFrom(type)) { int num3 = package.ReadInt(); object obj2 = Activator.CreateInstance(type); MethodInfo method = type3.GetMethod("Add"); for (int k = 0; k < num3; k++) { method.Invoke(obj2, new object[1] { ReadValueWithTypeFromZPackage(package, type.GenericTypeArguments[0]) }); } return obj2; } } ParameterInfo parameterInfo = (ParameterInfo)FormatterServices.GetUninitializedObject(typeof(ParameterInfo)); AccessTools.DeclaredField(typeof(ParameterInfo), "ClassImpl").SetValue(parameterInfo, type); List source = new List(); ZRpc.Deserialize(new ParameterInfo[2] { null, parameterInfo }, package, ref source); return source.First(); } } [PublicAPI] [HarmonyPatch] internal class VersionCheck { private static readonly HashSet versionChecks; private static readonly Dictionary notProcessedNames; public string Name; private string? displayName; private string? currentVersion; private string? minimumRequiredVersion; public bool ModRequired = true; private string? ReceivedCurrentVersion; private string? ReceivedMinimumRequiredVersion; private readonly List ValidatedClients = new List(); private ConfigSync? ConfigSync; public string DisplayName { get { return displayName ?? Name; } set { displayName = value; } } public string CurrentVersion { get { return currentVersion ?? "0.0.0"; } set { currentVersion = value; } } public string MinimumRequiredVersion { get { return minimumRequiredVersion ?? (ModRequired ? CurrentVersion : "0.0.0"); } set { minimumRequiredVersion = value; } } private static void PatchServerSync() { //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Expected O, but got Unknown Patches patchInfo = PatchProcessor.GetPatchInfo((MethodBase)AccessTools.DeclaredMethod(typeof(ZNet), "Awake", (Type[])null, (Type[])null)); if (patchInfo != null && patchInfo.Postfixes.Count((Patch p) => p.PatchMethod.DeclaringType == typeof(ConfigSync.RegisterRPCPatch)) > 0) { return; } Harmony val = new Harmony("org.bepinex.helpers.ServerSync"); foreach (Type item in from t in typeof(ConfigSync).GetNestedTypes(BindingFlags.NonPublic).Concat(new Type[1] { typeof(VersionCheck) }) where t.IsClass select t) { val.PatchAll(item); } } static VersionCheck() { versionChecks = new HashSet(); notProcessedNames = new Dictionary(); typeof(ThreadingHelper).GetMethod("StartSyncInvoke").Invoke(ThreadingHelper.Instance, new object[1] { new Action(PatchServerSync) }); } public VersionCheck(string name) { Name = name; ModRequired = true; versionChecks.Add(this); } public VersionCheck(ConfigSync configSync) { ConfigSync = configSync; Name = ConfigSync.Name; versionChecks.Add(this); } public void Initialize() { ReceivedCurrentVersion = null; ReceivedMinimumRequiredVersion = null; if (ConfigSync != null) { Name = ConfigSync.Name; DisplayName = ConfigSync.DisplayName; CurrentVersion = ConfigSync.CurrentVersion; MinimumRequiredVersion = ConfigSync.MinimumRequiredVersion; ModRequired = ConfigSync.ModRequired; } } private bool IsVersionOk() { if (ReceivedMinimumRequiredVersion == null || ReceivedCurrentVersion == null) { return !ModRequired; } bool flag = new System.Version(CurrentVersion) >= new System.Version(ReceivedMinimumRequiredVersion); bool flag2 = new System.Version(ReceivedCurrentVersion) >= new System.Version(MinimumRequiredVersion); return flag && flag2; } private string ErrorClient() { if (ReceivedMinimumRequiredVersion == null) { return DisplayName + " is not installed on the server."; } return (new System.Version(CurrentVersion) >= new System.Version(ReceivedMinimumRequiredVersion)) ? (DisplayName + " may not be higher than version " + ReceivedCurrentVersion + ". You have version " + CurrentVersion + ".") : (DisplayName + " needs to be at least version " + ReceivedMinimumRequiredVersion + ". You have version " + CurrentVersion + "."); } private string ErrorServer(ZRpc rpc) { return "Disconnect: The client (" + rpc.GetSocket().GetHostName() + ") doesn't have the correct " + DisplayName + " version " + MinimumRequiredVersion; } private string Error(ZRpc? rpc = null) { return (rpc == null) ? ErrorClient() : ErrorServer(rpc); } private static VersionCheck[] GetFailedClient() { return versionChecks.Where((VersionCheck check) => !check.IsVersionOk()).ToArray(); } private static VersionCheck[] GetFailedServer(ZRpc rpc) { return versionChecks.Where((VersionCheck check) => check.ModRequired && !check.ValidatedClients.Contains(rpc)).ToArray(); } private static void Logout() { Game.instance.Logout(true, true); AccessTools.DeclaredField(typeof(ZNet), "m_connectionStatus").SetValue(null, (object)(ConnectionStatus)3); } private static void DisconnectClient(ZRpc rpc) { rpc.Invoke("Error", new object[1] { 3 }); } private static void CheckVersion(ZRpc rpc, ZPackage pkg) { CheckVersion(rpc, pkg, null); } private static void CheckVersion(ZRpc rpc, ZPackage pkg, Action? original) { string text = pkg.ReadString(); string text2 = pkg.ReadString(); string text3 = pkg.ReadString(); bool flag = false; foreach (VersionCheck versionCheck in versionChecks) { if (!(text != versionCheck.Name)) { Debug.Log((object)("Received " + versionCheck.DisplayName + " version " + text3 + " and minimum version " + text2 + " from the " + (ZNet.instance.IsServer() ? "client" : "server") + ".")); versionCheck.ReceivedMinimumRequiredVersion = text2; versionCheck.ReceivedCurrentVersion = text3; if (ZNet.instance.IsServer() && versionCheck.IsVersionOk()) { versionCheck.ValidatedClients.Add(rpc); } flag = true; } } if (flag) { return; } pkg.SetPos(0); if (original != null) { original(rpc, pkg); if (pkg.GetPos() == 0) { notProcessedNames.Add(text, text3); } } } [HarmonyPatch(typeof(ZNet), "RPC_PeerInfo")] [HarmonyPrefix] private static bool RPC_PeerInfo(ZRpc rpc, ZNet __instance) { VersionCheck[] array = (__instance.IsServer() ? GetFailedServer(rpc) : GetFailedClient()); if (array.Length == 0) { return true; } VersionCheck[] array2 = array; foreach (VersionCheck versionCheck in array2) { Debug.LogWarning((object)versionCheck.Error(rpc)); } if (__instance.IsServer()) { DisconnectClient(rpc); } else { Logout(); } return false; } [HarmonyPatch(typeof(ZNet), "OnNewConnection")] [HarmonyPrefix] private static void RegisterAndCheckVersion(ZNetPeer peer, ZNet __instance) { //IL_018e: Unknown result type (might be due to invalid IL or missing references) //IL_0195: Expected O, but got Unknown notProcessedNames.Clear(); IDictionary dictionary = (IDictionary)typeof(ZRpc).GetField("m_functions", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(peer.m_rpc); if (dictionary.Contains(StringExtensionMethods.GetStableHashCode("ServerSync VersionCheck"))) { object obj = dictionary[StringExtensionMethods.GetStableHashCode("ServerSync VersionCheck")]; Action action = (Action)obj.GetType().GetField("m_action", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(obj); peer.m_rpc.Register("ServerSync VersionCheck", (Action)delegate(ZRpc rpc, ZPackage pkg) { CheckVersion(rpc, pkg, action); }); } else { peer.m_rpc.Register("ServerSync VersionCheck", (Action)CheckVersion); } foreach (VersionCheck versionCheck in versionChecks) { versionCheck.Initialize(); if (versionCheck.ModRequired || __instance.IsServer()) { Debug.Log((object)("Sending " + versionCheck.DisplayName + " version " + versionCheck.CurrentVersion + " and minimum version " + versionCheck.MinimumRequiredVersion + " to the " + (__instance.IsServer() ? "client" : "server") + ".")); ZPackage val = new ZPackage(); val.Write(versionCheck.Name); val.Write(versionCheck.MinimumRequiredVersion); val.Write(versionCheck.CurrentVersion); peer.m_rpc.Invoke("ServerSync VersionCheck", new object[1] { val }); } } } [HarmonyPatch(typeof(ZNet), "Disconnect")] [HarmonyPrefix] private static void RemoveDisconnected(ZNetPeer peer, ZNet __instance) { if (!__instance.IsServer()) { return; } foreach (VersionCheck versionCheck in versionChecks) { versionCheck.ValidatedClients.Remove(peer.m_rpc); } } [HarmonyPatch(typeof(FejdStartup), "ShowConnectError")] [HarmonyPostfix] private static void ShowConnectionError(FejdStartup __instance) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Invalid comparison between Unknown and I4 //IL_0186: Unknown result type (might be due to invalid IL or missing references) //IL_018b: Unknown result type (might be due to invalid IL or missing references) //IL_0199: Unknown result type (might be due to invalid IL or missing references) //IL_01de: Unknown result type (might be due to invalid IL or missing references) //IL_01ea: Unknown result type (might be due to invalid IL or missing references) //IL_01f8: Unknown result type (might be due to invalid IL or missing references) //IL_020a: Unknown result type (might be due to invalid IL or missing references) //IL_0219: Unknown result type (might be due to invalid IL or missing references) //IL_021e: Unknown result type (might be due to invalid IL or missing references) //IL_0229: Unknown result type (might be due to invalid IL or missing references) if (!__instance.m_connectionFailedPanel.activeSelf || (int)ZNet.GetConnectionStatus() != 3) { return; } bool flag = false; VersionCheck[] failedClient = GetFailedClient(); if (failedClient.Length != 0) { string text = string.Join("\n", failedClient.Select((VersionCheck check) => check.Error())); TMP_Text connectionFailedError = __instance.m_connectionFailedError; connectionFailedError.text = connectionFailedError.text + "\n" + text; flag = true; } foreach (KeyValuePair item in notProcessedNames.OrderBy, string>((KeyValuePair kv) => kv.Key)) { if (!__instance.m_connectionFailedError.text.Contains(item.Key)) { TMP_Text connectionFailedError2 = __instance.m_connectionFailedError; connectionFailedError2.text = connectionFailedError2.text + "\nServer expects you to have " + item.Key + " (Version: " + item.Value + ") installed."; flag = true; } } if (flag) { RectTransform component = ((Component)__instance.m_connectionFailedPanel.transform.Find("Image")).GetComponent(); Vector2 sizeDelta = component.sizeDelta; sizeDelta.x = 675f; component.sizeDelta = sizeDelta; __instance.m_connectionFailedError.ForceMeshUpdate(false, false); float num = __instance.m_connectionFailedError.renderedHeight + 105f; RectTransform component2 = ((Component)((Component)component).transform.Find("ButtonOk")).GetComponent(); component2.anchoredPosition = new Vector2(component2.anchoredPosition.x, component2.anchoredPosition.y - (num - component.sizeDelta.y) / 2f); sizeDelta = component.sizeDelta; sizeDelta.y = num; component.sizeDelta = sizeDelta; } } } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] internal sealed class IsReadOnlyAttribute : Attribute { } } namespace YamlDotNet { internal sealed class CultureInfoAdapter : CultureInfo { private readonly IFormatProvider provider; public CultureInfoAdapter(CultureInfo baseCulture, IFormatProvider provider) : base(baseCulture.Name) { this.provider = provider; } public override object? GetFormat(Type formatType) { return provider.GetFormat(formatType); } } internal static class Polyfills { [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static bool Contains(this string source, char c) { return source.IndexOf(c) != -1; } [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static bool EndsWith(this string source, char c) { if (source.Length > 0) { return source[source.Length - 1] == c; } return false; } [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static bool StartsWith(this string source, char c) { if (source.Length > 0) { return source[0] == c; } return false; } } internal static class PropertyInfoExtensions { public static object? ReadValue(this PropertyInfo property, object target) { return property.GetValue(target, null); } } internal static class ReflectionExtensions { private static readonly Func IsInstance = (PropertyInfo property) => !(property.GetMethod ?? property.SetMethod).IsStatic; private static readonly Func IsInstancePublic = (PropertyInfo property) => IsInstance(property) && (property.GetMethod ?? property.SetMethod).IsPublic; public static Type? BaseType(this Type type) { return type.GetTypeInfo().BaseType; } public static bool IsValueType(this Type type) { return type.GetTypeInfo().IsValueType; } public static bool IsGenericType(this Type type) { return type.GetTypeInfo().IsGenericType; } public static bool IsGenericTypeDefinition(this Type type) { return type.GetTypeInfo().IsGenericTypeDefinition; } public static Type? GetImplementationOfOpenGenericInterface(this Type type, Type openGenericType) { if (!openGenericType.IsGenericType || !openGenericType.IsInterface) { throw new ArgumentException("The type must be a generic type definition and an interface", "openGenericType"); } if (IsGenericDefinitionOfType(type, openGenericType)) { return type; } return type.FindInterfaces((Type t, object context) => IsGenericDefinitionOfType(t, context), openGenericType).FirstOrDefault(); static bool IsGenericDefinitionOfType(Type t, object? context) { if (t.IsGenericType) { return t.GetGenericTypeDefinition() == (Type)context; } return false; } } public static bool IsInterface(this Type type) { return type.GetTypeInfo().IsInterface; } public static bool IsEnum(this Type type) { return type.GetTypeInfo().IsEnum; } public static bool IsRequired(this MemberInfo member) { return member.GetCustomAttributes(inherit: true).Any((object x) => x.GetType().FullName == "System.Runtime.CompilerServices.RequiredMemberAttribute"); } public static bool HasDefaultConstructor(this Type type, bool allowPrivateConstructors) { BindingFlags bindingFlags = BindingFlags.Instance | BindingFlags.Public; if (allowPrivateConstructors) { bindingFlags |= BindingFlags.NonPublic; } if (!type.IsValueType) { return type.GetConstructor(bindingFlags, null, Type.EmptyTypes, null) != null; } return true; } public static bool IsAssignableFrom(this Type type, Type source) { return type.IsAssignableFrom(source.GetTypeInfo()); } public static bool IsAssignableFrom(this Type type, TypeInfo source) { return type.GetTypeInfo().IsAssignableFrom(source); } public static TypeCode GetTypeCode(this Type type) { if (type.IsEnum()) { type = Enum.GetUnderlyingType(type); } if (type == typeof(bool)) { return TypeCode.Boolean; } if (type == typeof(char)) { return TypeCode.Char; } if (type == typeof(sbyte)) { return TypeCode.SByte; } if (type == typeof(byte)) { return TypeCode.Byte; } if (type == typeof(short)) { return TypeCode.Int16; } if (type == typeof(ushort)) { return TypeCode.UInt16; } if (type == typeof(int)) { return TypeCode.Int32; } if (type == typeof(uint)) { return TypeCode.UInt32; } if (type == typeof(long)) { return TypeCode.Int64; } if (type == typeof(ulong)) { return TypeCode.UInt64; } if (type == typeof(float)) { return TypeCode.Single; } if (type == typeof(double)) { return TypeCode.Double; } if (type == typeof(decimal)) { return TypeCode.Decimal; } if (type == typeof(DateTime)) { return TypeCode.DateTime; } if (type == typeof(string)) { return TypeCode.String; } return TypeCode.Object; } public static bool IsDbNull(this object value) { return value?.GetType()?.FullName == "System.DBNull"; } public static Type[] GetGenericArguments(this Type type) { return type.GetTypeInfo().GenericTypeArguments; } public static PropertyInfo? GetPublicProperty(this Type type, string name) { return type.GetProperties(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public).FirstOrDefault((PropertyInfo p) => p.Name == name); } public static FieldInfo? GetPublicStaticField(this Type type, string name) { return type.GetRuntimeField(name); } public static IEnumerable GetProperties(this Type type, bool includeNonPublic) { Func predicate = (includeNonPublic ? IsInstance : IsInstancePublic); if (!type.IsInterface()) { return type.GetRuntimeProperties().Where(predicate); } return new Type[1] { type }.Concat(type.GetInterfaces()).SelectMany((Type i) => i.GetRuntimeProperties().Where(predicate)); } public static IEnumerable GetPublicProperties(this Type type) { return type.GetProperties(includeNonPublic: false); } public static IEnumerable GetPublicFields(this Type type) { return from f in type.GetRuntimeFields() where !f.IsStatic && f.IsPublic select f; } public static IEnumerable GetPublicStaticMethods(this Type type) { return from m in type.GetRuntimeMethods() where m.IsPublic && m.IsStatic select m; } public static MethodInfo GetPrivateStaticMethod(this Type type, string name) { return type.GetRuntimeMethods().FirstOrDefault((MethodInfo m) => !m.IsPublic && m.IsStatic && m.Name.Equals(name)) ?? throw new MissingMethodException("Expected to find a method named '" + name + "' in '" + type.FullName + "'."); } public static MethodInfo? GetPublicStaticMethod(this Type type, string name, params Type[] parameterTypes) { return type.GetRuntimeMethods().FirstOrDefault(delegate(MethodInfo m) { if (m.IsPublic && m.IsStatic && m.Name.Equals(name)) { ParameterInfo[] parameters = m.GetParameters(); if (parameters.Length == parameterTypes.Length) { return parameters.Zip(parameterTypes, (ParameterInfo pi, Type pt) => pi.ParameterType == pt).All((bool r) => r); } return false; } return false; }); } public static MethodInfo? GetPublicInstanceMethod(this Type type, string name) { return type.GetRuntimeMethods().FirstOrDefault((MethodInfo m) => m.IsPublic && !m.IsStatic && m.Name.Equals(name)); } public static MethodInfo? GetGetMethod(this PropertyInfo property, bool nonPublic) { MethodInfo methodInfo = property.GetMethod; if (!nonPublic && !methodInfo.IsPublic) { methodInfo = null; } return methodInfo; } public static MethodInfo? GetSetMethod(this PropertyInfo property) { return property.SetMethod; } public static IEnumerable GetInterfaces(this Type type) { return type.GetTypeInfo().ImplementedInterfaces; } public static bool IsInstanceOf(this Type type, object o) { if (!(o.GetType() == type)) { return o.GetType().GetTypeInfo().IsSubclassOf(type); } return true; } public static Attribute[] GetAllCustomAttributes(this PropertyInfo member) { return Attribute.GetCustomAttributes(member, typeof(TAttribute), inherit: true); } public static bool AcceptsNull(this MemberInfo member) { object[] customAttributes = member.DeclaringType.GetCustomAttributes(inherit: true); object obj = customAttributes.FirstOrDefault((object x) => x.GetType().FullName == "System.Runtime.CompilerServices.NullableContextAttribute"); int num = 0; if (obj != null) { Type type = obj.GetType(); PropertyInfo property = type.GetProperty("Flag"); num = (byte)property.GetValue(obj); } object[] customAttributes2 = member.GetCustomAttributes(inherit: true); object obj2 = customAttributes2.FirstOrDefault((object x) => x.GetType().FullName == "System.Runtime.CompilerServices.NullableAttribute"); PropertyInfo propertyInfo = (obj2?.GetType())?.GetProperty("NullableFlags"); byte[] source = (byte[])propertyInfo.GetValue(obj2); return source.Any((byte x) => x == 2) || num == 2; } } internal static class StandardRegexOptions { public const RegexOptions Compiled = RegexOptions.Compiled; } } namespace YamlDotNet.Serialization { internal abstract class BuilderSkeleton where TBuilder : BuilderSkeleton { internal INamingConvention namingConvention = NullNamingConvention.Instance; internal INamingConvention enumNamingConvention = NullNamingConvention.Instance; internal ITypeResolver typeResolver; internal readonly YamlAttributeOverrides overrides; internal readonly LazyComponentRegistrationList typeConverterFactories; internal readonly LazyComponentRegistrationList typeInspectorFactories; internal bool ignoreFields; internal bool includeNonPublicProperties; internal Settings settings; internal YamlFormatter yamlFormatter = YamlFormatter.Default; protected abstract TBuilder Self { get; } internal BuilderSkeleton(ITypeResolver typeResolver) { overrides = new YamlAttributeOverrides(); typeConverterFactories = new LazyComponentRegistrationList { { typeof(YamlDotNet.Serialization.Converters.GuidConverter), (Nothing _) => new YamlDotNet.Serialization.Converters.GuidConverter(jsonCompatible: false) }, { typeof(SystemTypeConverter), (Nothing _) => new SystemTypeConverter() } }; typeInspectorFactories = new LazyComponentRegistrationList(); this.typeResolver = typeResolver ?? throw new ArgumentNullException("typeResolver"); settings = new Settings(); } public TBuilder IgnoreFields() { ignoreFields = true; return Self; } public TBuilder IncludeNonPublicProperties() { includeNonPublicProperties = true; return Self; } public TBuilder EnablePrivateConstructors() { settings.AllowPrivateConstructors = true; return Self; } public TBuilder WithNamingConvention(INamingConvention namingConvention) { this.namingConvention = namingConvention ?? throw new ArgumentNullException("namingConvention"); return Self; } public TBuilder WithEnumNamingConvention(INamingConvention enumNamingConvention) { this.enumNamingConvention = enumNamingConvention; return Self; } public TBuilder WithTypeResolver(ITypeResolver typeResolver) { this.typeResolver = typeResolver ?? throw new ArgumentNullException("typeResolver"); return Self; } public abstract TBuilder WithTagMapping(TagName tag, Type type); public TBuilder WithAttributeOverride(Expression> propertyAccessor, Attribute attribute) { overrides.Add(propertyAccessor, attribute); return Self; } public TBuilder WithAttributeOverride(Type type, string member, Attribute attribute) { overrides.Add(type, member, attribute); return Self; } public TBuilder WithTypeConverter(IYamlTypeConverter typeConverter) { return WithTypeConverter(typeConverter, delegate(IRegistrationLocationSelectionSyntax w) { w.OnTop(); }); } public TBuilder WithTypeConverter(IYamlTypeConverter typeConverter, Action> where) { if (typeConverter == null) { throw new ArgumentNullException("typeConverter"); } if (where == null) { throw new ArgumentNullException("where"); } where(typeConverterFactories.CreateRegistrationLocationSelector(typeConverter.GetType(), (Nothing _) => typeConverter)); return Self; } public TBuilder WithTypeConverter(WrapperFactory typeConverterFactory, Action> where) where TYamlTypeConverter : IYamlTypeConverter { if (typeConverterFactory == null) { throw new ArgumentNullException("typeConverterFactory"); } if (where == null) { throw new ArgumentNullException("where"); } where(typeConverterFactories.CreateTrackingRegistrationLocationSelector(typeof(TYamlTypeConverter), (IYamlTypeConverter wrapped, Nothing _) => typeConverterFactory(wrapped))); return Self; } public TBuilder WithoutTypeConverter() where TYamlTypeConverter : IYamlTypeConverter { return WithoutTypeConverter(typeof(TYamlTypeConverter)); } public TBuilder WithoutTypeConverter(Type converterType) { if (converterType == null) { throw new ArgumentNullException("converterType"); } typeConverterFactories.Remove(converterType); return Self; } public TBuilder WithTypeInspector(Func typeInspectorFactory) where TTypeInspector : ITypeInspector { return WithTypeInspector(typeInspectorFactory, delegate(IRegistrationLocationSelectionSyntax w) { w.OnTop(); }); } public TBuilder WithTypeInspector(Func typeInspectorFactory, Action> where) where TTypeInspector : ITypeInspector { if (typeInspectorFactory == null) { throw new ArgumentNullException("typeInspectorFactory"); } if (where == null) { throw new ArgumentNullException("where"); } where(typeInspectorFactories.CreateRegistrationLocationSelector(typeof(TTypeInspector), (ITypeInspector inner) => typeInspectorFactory(inner))); return Self; } public TBuilder WithTypeInspector(WrapperFactory typeInspectorFactory, Action> where) where TTypeInspector : ITypeInspector { if (typeInspectorFactory == null) { throw new ArgumentNullException("typeInspectorFactory"); } if (where == null) { throw new ArgumentNullException("where"); } where(typeInspectorFactories.CreateTrackingRegistrationLocationSelector(typeof(TTypeInspector), (ITypeInspector wrapped, ITypeInspector inner) => typeInspectorFactory(wrapped, inner))); return Self; } public TBuilder WithoutTypeInspector() where TTypeInspector : ITypeInspector { return WithoutTypeInspector(typeof(TTypeInspector)); } public TBuilder WithoutTypeInspector(Type inspectorType) { if (inspectorType == null) { throw new ArgumentNullException("inspectorType"); } typeInspectorFactories.Remove(inspectorType); return Self; } public TBuilder WithYamlFormatter(YamlFormatter formatter) { yamlFormatter = formatter ?? throw new ArgumentNullException("formatter"); return Self; } protected IEnumerable BuildTypeConverters() { return typeConverterFactories.BuildComponentList(); } } internal delegate TComponent WrapperFactory(TComponentBase wrapped) where TComponent : TComponentBase; internal delegate TComponent WrapperFactory(TComponentBase wrapped, TArgument argument) where TComponent : TComponentBase; [Flags] internal enum DefaultValuesHandling { Preserve = 0, OmitNull = 1, OmitDefaults = 2, OmitEmptyCollections = 4 } internal sealed class Deserializer : IDeserializer { private readonly IValueDeserializer valueDeserializer; public Deserializer() : this(new DeserializerBuilder().BuildValueDeserializer()) { } private Deserializer(IValueDeserializer valueDeserializer) { this.valueDeserializer = valueDeserializer ?? throw new ArgumentNullException("valueDeserializer"); } public static Deserializer FromValueDeserializer(IValueDeserializer valueDeserializer) { return new Deserializer(valueDeserializer); } public T Deserialize(string input) { using StringReader input2 = new StringReader(input); return Deserialize(input2); } public T Deserialize(TextReader input) { return Deserialize(new Parser(input)); } public T Deserialize(IParser parser) { return (T)Deserialize(parser, typeof(T)); } public object? Deserialize(string input) { return Deserialize(input); } public object? Deserialize(TextReader input) { return Deserialize(input); } public object? Deserialize(IParser parser) { return Deserialize(parser); } public object? Deserialize(string input, Type type) { using StringReader input2 = new StringReader(input); return Deserialize(input2, type); } public object? Deserialize(TextReader input, Type type) { return Deserialize(new Parser(input), type); } public object? Deserialize(IParser parser, Type type) { if (parser == null) { throw new ArgumentNullException("parser"); } if (type == null) { throw new ArgumentNullException("type"); } YamlDotNet.Core.Events.StreamStart @event; bool flag = parser.TryConsume(out @event); YamlDotNet.Core.Events.DocumentStart event2; bool flag2 = parser.TryConsume(out event2); object result = null; if (!parser.Accept(out var _) && !parser.Accept(out var _)) { using SerializerState serializerState = new SerializerState(); result = valueDeserializer.DeserializeValue(parser, type, serializerState, valueDeserializer); serializerState.OnDeserialization(); } if (flag2) { parser.Consume(); } if (flag) { parser.Consume(); } return result; } } internal sealed class DeserializerBuilder : BuilderSkeleton { private Lazy objectFactory; private readonly LazyComponentRegistrationList nodeDeserializerFactories; private readonly LazyComponentRegistrationList nodeTypeResolverFactories; private readonly Dictionary tagMappings; private readonly Dictionary typeMappings; private readonly ITypeConverter typeConverter; private bool ignoreUnmatched; private bool duplicateKeyChecking; private bool attemptUnknownTypeDeserialization; private bool enforceNullability; private bool caseInsensitivePropertyMatching; private bool enforceRequiredProperties; protected override DeserializerBuilder Self => this; public DeserializerBuilder() : base((ITypeResolver)new StaticTypeResolver()) { typeMappings = new Dictionary(); objectFactory = new Lazy(() => new DefaultObjectFactory(typeMappings, settings), isThreadSafe: true); tagMappings = new Dictionary { { FailsafeSchema.Tags.Map, typeof(Dictionary) }, { FailsafeSchema.Tags.Str, typeof(string) }, { JsonSchema.Tags.Bool, typeof(bool) }, { JsonSchema.Tags.Float, typeof(double) }, { JsonSchema.Tags.Int, typeof(int) }, { DefaultSchema.Tags.Timestamp, typeof(DateTime) } }; typeInspectorFactories.Add(typeof(CachedTypeInspector), (ITypeInspector inner) => new CachedTypeInspector(inner)); typeInspectorFactories.Add(typeof(NamingConventionTypeInspector), (ITypeInspector inner) => (!(namingConvention is NullNamingConvention)) ? new NamingConventionTypeInspector(inner, namingConvention) : inner); typeInspectorFactories.Add(typeof(YamlAttributesTypeInspector), (ITypeInspector inner) => new YamlAttributesTypeInspector(inner)); typeInspectorFactories.Add(typeof(YamlAttributeOverridesInspector), (ITypeInspector inner) => (overrides == null) ? inner : new YamlAttributeOverridesInspector(inner, overrides.Clone())); typeInspectorFactories.Add(typeof(ReadableAndWritablePropertiesTypeInspector), (ITypeInspector inner) => new ReadableAndWritablePropertiesTypeInspector(inner)); nodeDeserializerFactories = new LazyComponentRegistrationList { { typeof(YamlConvertibleNodeDeserializer), (Nothing _) => new YamlConvertibleNodeDeserializer(objectFactory.Value) }, { typeof(YamlSerializableNodeDeserializer), (Nothing _) => new YamlSerializableNodeDeserializer(objectFactory.Value) }, { typeof(TypeConverterNodeDeserializer), (Nothing _) => new TypeConverterNodeDeserializer(BuildTypeConverters()) }, { typeof(NullNodeDeserializer), (Nothing _) => new NullNodeDeserializer() }, { typeof(ScalarNodeDeserializer), (Nothing _) => new ScalarNodeDeserializer(attemptUnknownTypeDeserialization, typeConverter, BuildTypeInspector(), yamlFormatter, enumNamingConvention) }, { typeof(ArrayNodeDeserializer), (Nothing _) => new ArrayNodeDeserializer(enumNamingConvention, BuildTypeInspector()) }, { typeof(DictionaryNodeDeserializer), (Nothing _) => new DictionaryNodeDeserializer(objectFactory.Value, duplicateKeyChecking) }, { typeof(CollectionNodeDeserializer), (Nothing _) => new CollectionNodeDeserializer(objectFactory.Value, enumNamingConvention, BuildTypeInspector()) }, { typeof(EnumerableNodeDeserializer), (Nothing _) => new EnumerableNodeDeserializer() }, { typeof(ObjectNodeDeserializer), (Nothing _) => new ObjectNodeDeserializer(objectFactory.Value, BuildTypeInspector(), ignoreUnmatched, duplicateKeyChecking, typeConverter, enumNamingConvention, enforceNullability, caseInsensitivePropertyMatching, enforceRequiredProperties, BuildTypeConverters()) }, { typeof(FsharpListNodeDeserializer), (Nothing _) => new FsharpListNodeDeserializer(BuildTypeInspector(), enumNamingConvention) } }; nodeTypeResolverFactories = new LazyComponentRegistrationList { { typeof(MappingNodeTypeResolver), (Nothing _) => new MappingNodeTypeResolver(typeMappings) }, { typeof(YamlConvertibleTypeResolver), (Nothing _) => new YamlConvertibleTypeResolver() }, { typeof(YamlSerializableTypeResolver), (Nothing _) => new YamlSerializableTypeResolver() }, { typeof(TagNodeTypeResolver), (Nothing _) => new TagNodeTypeResolver(tagMappings) }, { typeof(PreventUnknownTagsNodeTypeResolver), (Nothing _) => new PreventUnknownTagsNodeTypeResolver() }, { typeof(DefaultContainersNodeTypeResolver), (Nothing _) => new DefaultContainersNodeTypeResolver() } }; typeConverter = new ReflectionTypeConverter(); } public ITypeInspector BuildTypeInspector() { ITypeInspector typeInspector = new WritablePropertiesTypeInspector(typeResolver, includeNonPublicProperties); if (!ignoreFields) { typeInspector = new CompositeTypeInspector(new ReadableFieldsTypeInspector(typeResolver), typeInspector); } return typeInspectorFactories.BuildComponentChain(typeInspector); } public DeserializerBuilder WithAttemptingUnquotedStringTypeDeserialization() { attemptUnknownTypeDeserialization = true; return this; } public DeserializerBuilder WithObjectFactory(IObjectFactory objectFactory) { if (objectFactory == null) { throw new ArgumentNullException("objectFactory"); } this.objectFactory = new Lazy(() => objectFactory, isThreadSafe: true); return this; } public DeserializerBuilder WithObjectFactory(Func objectFactory) { if (objectFactory == null) { throw new ArgumentNullException("objectFactory"); } return WithObjectFactory(new LambdaObjectFactory(objectFactory)); } public DeserializerBuilder WithNodeDeserializer(INodeDeserializer nodeDeserializer) { return WithNodeDeserializer(nodeDeserializer, delegate(IRegistrationLocationSelectionSyntax w) { w.OnTop(); }); } public DeserializerBuilder WithNodeDeserializer(INodeDeserializer nodeDeserializer, Action> where) { if (nodeDeserializer == null) { throw new ArgumentNullException("nodeDeserializer"); } if (where == null) { throw new ArgumentNullException("where"); } where(nodeDeserializerFactories.CreateRegistrationLocationSelector(nodeDeserializer.GetType(), (Nothing _) => nodeDeserializer)); return this; } public DeserializerBuilder WithNodeDeserializer(WrapperFactory nodeDeserializerFactory, Action> where) where TNodeDeserializer : INodeDeserializer { if (nodeDeserializerFactory == null) { throw new ArgumentNullException("nodeDeserializerFactory"); } if (where == null) { throw new ArgumentNullException("where"); } where(nodeDeserializerFactories.CreateTrackingRegistrationLocationSelector(typeof(TNodeDeserializer), (INodeDeserializer wrapped, Nothing _) => nodeDeserializerFactory(wrapped))); return this; } public DeserializerBuilder WithoutNodeDeserializer() where TNodeDeserializer : INodeDeserializer { return WithoutNodeDeserializer(typeof(TNodeDeserializer)); } public DeserializerBuilder WithoutNodeDeserializer(Type nodeDeserializerType) { if (nodeDeserializerType == null) { throw new ArgumentNullException("nodeDeserializerType"); } nodeDeserializerFactories.Remove(nodeDeserializerType); return this; } public DeserializerBuilder WithTypeDiscriminatingNodeDeserializer(Action configureTypeDiscriminatingNodeDeserializerOptions, int maxDepth = -1, int maxLength = -1) { TypeDiscriminatingNodeDeserializerOptions typeDiscriminatingNodeDeserializerOptions = new TypeDiscriminatingNodeDeserializerOptions(); configureTypeDiscriminatingNodeDeserializerOptions(typeDiscriminatingNodeDeserializerOptions); TypeDiscriminatingNodeDeserializer nodeDeserializer = new TypeDiscriminatingNodeDeserializer(nodeDeserializerFactories.BuildComponentList(), typeDiscriminatingNodeDeserializerOptions.discriminators, maxDepth, maxLength); return WithNodeDeserializer(nodeDeserializer, delegate(IRegistrationLocationSelectionSyntax s) { s.Before(); }); } public DeserializerBuilder WithNodeTypeResolver(INodeTypeResolver nodeTypeResolver) { return WithNodeTypeResolver(nodeTypeResolver, delegate(IRegistrationLocationSelectionSyntax w) { w.OnTop(); }); } public DeserializerBuilder WithNodeTypeResolver(INodeTypeResolver nodeTypeResolver, Action> where) { if (nodeTypeResolver == null) { throw new ArgumentNullException("nodeTypeResolver"); } if (where == null) { throw new ArgumentNullException("where"); } where(nodeTypeResolverFactories.CreateRegistrationLocationSelector(nodeTypeResolver.GetType(), (Nothing _) => nodeTypeResolver)); return this; } public DeserializerBuilder WithNodeTypeResolver(WrapperFactory nodeTypeResolverFactory, Action> where) where TNodeTypeResolver : INodeTypeResolver { if (nodeTypeResolverFactory == null) { throw new ArgumentNullException("nodeTypeResolverFactory"); } if (where == null) { throw new ArgumentNullException("where"); } where(nodeTypeResolverFactories.CreateTrackingRegistrationLocationSelector(typeof(TNodeTypeResolver), (INodeTypeResolver wrapped, Nothing _) => nodeTypeResolverFactory(wrapped))); return this; } public DeserializerBuilder WithCaseInsensitivePropertyMatching() { caseInsensitivePropertyMatching = true; return this; } public DeserializerBuilder WithEnforceNullability() { enforceNullability = true; return this; } public DeserializerBuilder WithEnforceRequiredMembers() { enforceRequiredProperties = true; return this; } public DeserializerBuilder WithoutNodeTypeResolver() where TNodeTypeResolver : INodeTypeResolver { return WithoutNodeTypeResolver(typeof(TNodeTypeResolver)); } public DeserializerBuilder WithoutNodeTypeResolver(Type nodeTypeResolverType) { if (nodeTypeResolverType == null) { throw new ArgumentNullException("nodeTypeResolverType"); } nodeTypeResolverFactories.Remove(nodeTypeResolverType); return this; } public override DeserializerBuilder WithTagMapping(TagName tag, Type type) { if (tag.IsEmpty) { throw new ArgumentException("Non-specific tags cannot be maped"); } if (type == null) { throw new ArgumentNullException("type"); } if (tagMappings.TryGetValue(tag, out Type value)) { throw new ArgumentException($"Type already has a registered type '{value.FullName}' for tag '{tag}'", "tag"); } tagMappings.Add(tag, type); return this; } public DeserializerBuilder WithTypeMapping() where TConcrete : TInterface { Type typeFromHandle = typeof(TInterface); Type typeFromHandle2 = typeof(TConcrete); if (!typeFromHandle.IsAssignableFrom(typeFromHandle2)) { throw new InvalidOperationException("The type '" + typeFromHandle2.Name + "' does not implement interface '" + typeFromHandle.Name + "'."); } if (!DictionaryExtensions.TryAdd(typeMappings, typeFromHandle, typeFromHandle2)) { typeMappings[typeFromHandle] = typeFromHandle2; } return this; } public DeserializerBuilder WithoutTagMapping(TagName tag) { if (tag.IsEmpty) { throw new ArgumentException("Non-specific tags cannot be maped"); } if (!tagMappings.Remove(tag)) { throw new KeyNotFoundException($"Tag '{tag}' is not registered"); } return this; } public DeserializerBuilder IgnoreUnmatchedProperties() { ignoreUnmatched = true; return this; } public DeserializerBuilder WithDuplicateKeyChecking() { duplicateKeyChecking = true; return this; } public IDeserializer Build() { if (FsharpHelper.Instance == null) { FsharpHelper.Instance = new DefaultFsharpHelper(); } return Deserializer.FromValueDeserializer(BuildValueDeserializer()); } public IValueDeserializer BuildValueDeserializer() { return new AliasValueDeserializer(new NodeValueDeserializer(nodeDeserializerFactories.BuildComponentList(), nodeTypeResolverFactories.BuildComponentList(), typeConverter, enumNamingConvention, BuildTypeInspector())); } } internal sealed class EmissionPhaseObjectGraphVisitorArgs { private readonly IEnumerable> preProcessingPhaseVisitors; public IObjectGraphVisitor InnerVisitor { get; private set; } public IEventEmitter EventEmitter { get; private set; } public ObjectSerializer NestedObjectSerializer { get; private set; } public IEnumerable TypeConverters { get; private set; } public EmissionPhaseObjectGraphVisitorArgs(IObjectGraphVisitor innerVisitor, IEventEmitter eventEmitter, IEnumerable> preProcessingPhaseVisitors, IEnumerable typeConverters, ObjectSerializer nestedObjectSerializer) { InnerVisitor = innerVisitor ?? throw new ArgumentNullException("innerVisitor"); EventEmitter = eventEmitter ?? throw new ArgumentNullException("eventEmitter"); this.preProcessingPhaseVisitors = preProcessingPhaseVisitors ?? throw new ArgumentNullException("preProcessingPhaseVisitors"); TypeConverters = typeConverters ?? throw new ArgumentNullException("typeConverters"); NestedObjectSerializer = nestedObjectSerializer ?? throw new ArgumentNullException("nestedObjectSerializer"); } public T GetPreProcessingPhaseObjectGraphVisitor() where T : IObjectGraphVisitor { return preProcessingPhaseVisitors.OfType().Single(); } } internal abstract class EventInfo { public IObjectDescriptor Source { get; } protected EventInfo(IObjectDescriptor source) { Source = source ?? throw new ArgumentNullException("source"); } } internal class AliasEventInfo : EventInfo { public AnchorName Alias { get; } public bool NeedsExpansion { get; set; } public AliasEventInfo(IObjectDescriptor source, AnchorName alias) : base(source) { if (alias.IsEmpty) { throw new ArgumentNullException("alias"); } Alias = alias; } } internal class ObjectEventInfo : EventInfo { public AnchorName Anchor { get; set; } public TagName Tag { get; set; } protected ObjectEventInfo(IObjectDescriptor source) : base(source) { } } internal sealed class ScalarEventInfo : ObjectEventInfo { public string RenderedValue { get; set; } public ScalarStyle Style { get; set; } public bool IsPlainImplicit { get; set; } public bool IsQuotedImplicit { get; set; } public ScalarEventInfo(IObjectDescriptor source) : base(source) { Style = source.ScalarStyle; RenderedValue = string.Empty; } } internal sealed class MappingStartEventInfo : ObjectEventInfo { public bool IsImplicit { get; set; } public MappingStyle Style { get; set; } public MappingStartEventInfo(IObjectDescriptor source) : base(source) { } } internal sealed class MappingEndEventInfo : EventInfo { public MappingEndEventInfo(IObjectDescriptor source) : base(source) { } } internal sealed class SequenceStartEventInfo : ObjectEventInfo { public bool IsImplicit { get; set; } public SequenceStyle Style { get; set; } public SequenceStartEventInfo(IObjectDescriptor source) : base(source) { } } internal sealed class SequenceEndEventInfo : EventInfo { public SequenceEndEventInfo(IObjectDescriptor source) : base(source) { } } internal interface IAliasProvider { AnchorName GetAlias(object target); } internal interface IDeserializer { T Deserialize(string input); T Deserialize(TextReader input); T Deserialize(IParser parser); object? Deserialize(string input); object? Deserialize(TextReader input); object? Deserialize(IParser parser); object? Deserialize(string input, Type type); object? Deserialize(TextReader input, Type type); object? Deserialize(IParser parser, Type type); } internal interface IEventEmitter { void Emit(AliasEventInfo eventInfo, IEmitter emitter); void Emit(ScalarEventInfo eventInfo, IEmitter emitter); void Emit(MappingStartEventInfo eventInfo, IEmitter emitter); void Emit(MappingEndEventInfo eventInfo, IEmitter emitter); void Emit(SequenceStartEventInfo eventInfo, IEmitter emitter); void Emit(SequenceEndEventInfo eventInfo, IEmitter emitter); } internal interface INamingConvention { string Apply(string value); string Reverse(string value); } internal interface INodeDeserializer { bool Deserialize(IParser reader, Type expectedType, Func nestedObjectDeserializer, out object? value, ObjectDeserializer rootDeserializer); } internal interface INodeTypeResolver { bool Resolve(NodeEvent? nodeEvent, ref Type currentType); } internal interface IObjectAccessor { void Set(string name, object target, object value); object? Read(string name, object target); } internal interface IObjectDescriptor { object? Value { get; } Type Type { get; } Type StaticType { get; } ScalarStyle ScalarStyle { get; } } internal static class ObjectDescriptorExtensions { public static object NonNullValue(this IObjectDescriptor objectDescriptor) { return objectDescriptor.Value ?? throw new InvalidOperationException("Attempted to use a IObjectDescriptor of type '" + objectDescriptor.Type.FullName + "' whose Value is null at a point whete it is invalid to do so. This may indicate a bug in YamlDotNet."); } } internal interface IObjectFactory { object Create(Type type); object? CreatePrimitive(Type type); bool GetDictionary(IObjectDescriptor descriptor, out IDictionary? dictionary, out Type[]? genericArguments); Type GetValueType(Type type); void ExecuteOnDeserializing(object value); void ExecuteOnDeserialized(object value); void ExecuteOnSerializing(object value); void ExecuteOnSerialized(object value); } internal interface IObjectGraphTraversalStrategy { void Traverse(IObjectDescriptor graph, IObjectGraphVisitor visitor, TContext context, ObjectSerializer serializer); } internal interface IObjectGraphVisitor { bool Enter(IPropertyDescriptor? propertyDescriptor, IObjectDescriptor value, TContext context, ObjectSerializer serializer); bool EnterMapping(IObjectDescriptor key, IObjectDescriptor value, TContext context, ObjectSerializer serializer); bool EnterMapping(IPropertyDescriptor key, IObjectDescriptor value, TContext context, ObjectSerializer serializer); void VisitScalar(IObjectDescriptor scalar, TContext context, ObjectSerializer serializer); void VisitMappingStart(IObjectDescriptor mapping, Type keyType, Type valueType, TContext context, ObjectSerializer serializer); void VisitMappingEnd(IObjectDescriptor mapping, TContext context, ObjectSerializer serializer); void VisitSequenceStart(IObjectDescriptor sequence, Type elementType, TContext context, ObjectSerializer serializer); void VisitSequenceEnd(IObjectDescriptor sequence, TContext context, ObjectSerializer serializer); } internal interface IPropertyDescriptor { string Name { get; } bool AllowNulls { get; } bool CanWrite { get; } Type Type { get; } Type? TypeOverride { get; set; } int Order { get; set; } ScalarStyle ScalarStyle { get; set; } bool Required { get; } Type? ConverterType { get; } T? GetCustomAttribute() where T : Attribute; IObjectDescriptor Read(object target); void Write(object target, object? value); } internal interface IRegistrationLocationSelectionSyntax { void InsteadOf() where TRegistrationType : TBaseRegistrationType; void Before() where TRegistrationType : TBaseRegistrationType; void After() where TRegistrationType : TBaseRegistrationType; void OnTop(); void OnBottom(); } internal interface ITrackingRegistrationLocationSelectionSyntax { void InsteadOf() where TRegistrationType : TBaseRegistrationType; } internal interface ISerializer { string Serialize(object? graph); string Serialize(object? graph, Type type); void Serialize(TextWriter writer, object? graph); void Serialize(TextWriter writer, object? graph, Type type); void Serialize(IEmitter emitter, object? graph); void Serialize(IEmitter emitter, object? graph, Type type); } internal interface ITypeInspector { IEnumerable GetProperties(Type type, object? container); IPropertyDescriptor GetProperty(Type type, object? container, string name, [MaybeNullWhen(true)] bool ignoreUnmatched, bool caseInsensitivePropertyMatching); string GetEnumName(Type enumType, string name); string GetEnumValue(object enumValue); } internal interface ITypeResolver { Type Resolve(Type staticType, object? actualValue); } internal interface IValueDeserializer { object? DeserializeValue(IParser parser, Type expectedType, SerializerState state, IValueDeserializer nestedObjectDeserializer); } internal interface IValuePromise { event Action ValueAvailable; } internal interface IValueSerializer { void SerializeValue(IEmitter emitter, object? value, Type? type); } internal interface IYamlConvertible { void Read(IParser parser, Type expectedType, ObjectDeserializer nestedObjectDeserializer); void Write(IEmitter emitter, ObjectSerializer nestedObjectSerializer); } internal delegate object? ObjectDeserializer(Type type); internal delegate void ObjectSerializer(object? value, Type? type = null); [Obsolete("Please use IYamlConvertible instead")] internal interface IYamlSerializable { void ReadYaml(IParser parser); void WriteYaml(IEmitter emitter); } internal interface IYamlTypeConverter { bool Accepts(Type type); object? ReadYaml(IParser parser, Type type, ObjectDeserializer rootDeserializer); void WriteYaml(IEmitter emitter, object? value, Type type, ObjectSerializer serializer); } internal sealed class LazyComponentRegistrationList : IEnumerable>, IEnumerable { public sealed class LazyComponentRegistration { public readonly Type ComponentType; public readonly Func Factory; public LazyComponentRegistration(Type componentType, Func factory) { ComponentType = componentType; Factory = factory; } } public sealed class TrackingLazyComponentRegistration { public readonly Type ComponentType; public readonly Func Factory; public TrackingLazyComponentRegistration(Type componentType, Func factory) { ComponentType = componentType; Factory = factory; } } private class RegistrationLocationSelector : IRegistrationLocationSelectionSyntax { private readonly LazyComponentRegistrationList registrations; private readonly LazyComponentRegistration newRegistration; public RegistrationLocationSelector(LazyComponentRegistrationList registrations, LazyComponentRegistration newRegistration) { this.registrations = registrations; this.newRegistration = newRegistration; } void IRegistrationLocationSelectionSyntax.InsteadOf() { if (newRegistration.ComponentType != typeof(TRegistrationType)) { registrations.EnsureNoDuplicateRegistrationType(newRegistration.ComponentType); } int index = registrations.EnsureRegistrationExists(); registrations.entries[index] = newRegistration; } void IRegistrationLocationSelectionSyntax.After() { registrations.EnsureNoDuplicateRegistrationType(newRegistration.ComponentType); int num = registrations.EnsureRegistrationExists(); registrations.entries.Insert(num + 1, newRegistration); } void IRegistrationLocationSelectionSyntax.Before() { registrations.EnsureNoDuplicateRegistrationType(newRegistration.ComponentType); int index = registrations.EnsureRegistrationExists(); registrations.entries.Insert(index, newRegistration); } void IRegistrationLocationSelectionSyntax.OnBottom() { registrations.EnsureNoDuplicateRegistrationType(newRegistration.ComponentType); registrations.entries.Add(newRegistration); } void IRegistrationLocationSelectionSyntax.OnTop() { registrations.EnsureNoDuplicateRegistrationType(newRegistration.ComponentType); registrations.entries.Insert(0, newRegistration); } } private class TrackingRegistrationLocationSelector : ITrackingRegistrationLocationSelectionSyntax { private readonly LazyComponentRegistrationList registrations; private readonly TrackingLazyComponentRegistration newRegistration; public TrackingRegistrationLocationSelector(LazyComponentRegistrationList registrations, TrackingLazyComponentRegistration newRegistration) { this.registrations = registrations; this.newRegistration = newRegistration; } void ITrackingRegistrationLocationSelectionSyntax.InsteadOf() { if (newRegistration.ComponentType != typeof(TRegistrationType)) { registrations.EnsureNoDuplicateRegistrationType(newRegistration.ComponentType); } int index = registrations.EnsureRegistrationExists(); Func innerComponentFactory = registrations.entries[index].Factory; registrations.entries[index] = new LazyComponentRegistration(newRegistration.ComponentType, (TArgument arg) => newRegistration.Factory(innerComponentFactory(arg), arg)); } } private readonly List entries = new List(); public int Count => entries.Count; public IEnumerable> InReverseOrder { get { int i = entries.Count - 1; while (i >= 0) { yield return entries[i].Factory; int num = i - 1; i = num; } } } public LazyComponentRegistrationList Clone() { LazyComponentRegistrationList lazyComponentRegistrationList = new LazyComponentRegistrationList(); foreach (LazyComponentRegistration entry in entries) { lazyComponentRegistrationList.entries.Add(entry); } return lazyComponentRegistrationList; } public void Clear() { entries.Clear(); } public void Add(Type componentType, Func factory) { entries.Add(new LazyComponentRegistration(componentType, factory)); } public void Remove(Type componentType) { for (int i = 0; i < entries.Count; i++) { if (entries[i].ComponentType == componentType) { entries.RemoveAt(i); return; } } throw new KeyNotFoundException("A component registration of type '" + componentType.FullName + "' was not found."); } public IRegistrationLocationSelectionSyntax CreateRegistrationLocationSelector(Type componentType, Func factory) { return new RegistrationLocationSelector(this, new LazyComponentRegistration(componentType, factory)); } public ITrackingRegistrationLocationSelectionSyntax CreateTrackingRegistrationLocationSelector(Type componentType, Func factory) { return new TrackingRegistrationLocationSelector(this, new TrackingLazyComponentRegistration(componentType, factory)); } public IEnumerator> GetEnumerator() { return entries.Select((LazyComponentRegistration e) => e.Factory).GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } private int IndexOfRegistration(Type registrationType) { for (int i = 0; i < entries.Count; i++) { if (registrationType == entries[i].ComponentType) { return i; } } return -1; } private void EnsureNoDuplicateRegistrationType(Type componentType) { if (IndexOfRegistration(componentType) != -1) { throw new InvalidOperationException("A component of type '" + componentType.FullName + "' has already been registered."); } } private int EnsureRegistrationExists() { int num = IndexOfRegistration(typeof(TRegistrationType)); if (num == -1) { throw new InvalidOperationException("A component of type '" + typeof(TRegistrationType).FullName + "' has not been registered."); } return num; } } internal static class LazyComponentRegistrationListExtensions { public static TComponent BuildComponentChain(this LazyComponentRegistrationList registrations, TComponent innerComponent) { return registrations.InReverseOrder.Aggregate(innerComponent, (TComponent inner, Func factory) => factory(inner)); } public static TComponent BuildComponentChain(this LazyComponentRegistrationList registrations, TComponent innerComponent, Func argumentBuilder) { return registrations.InReverseOrder.Aggregate(innerComponent, (TComponent inner, Func factory) => factory(argumentBuilder(inner))); } public static List BuildComponentList(this LazyComponentRegistrationList registrations) { return registrations.Select((Func factory) => factory(default(Nothing))).ToList(); } public static List BuildComponentList(this LazyComponentRegistrationList registrations, TArgument argument) { return registrations.Select((Func factory) => factory(argument)).ToList(); } } [StructLayout(LayoutKind.Sequential, Size = 1)] internal struct Nothing { } internal sealed class ObjectDescriptor : IObjectDescriptor { public object? Value { get; private set; } public Type Type { get; private set; } public Type StaticType { get; private set; } public ScalarStyle ScalarStyle { get; private set; } public ObjectDescriptor(object? value, Type type, Type staticType) : this(value, type, staticType, ScalarStyle.Any) { } public ObjectDescriptor(object? value, Type type, Type staticType, ScalarStyle scalarStyle) { Value = value; Type = type ?? throw new ArgumentNullException("type"); StaticType = staticType ?? throw new ArgumentNullException("staticType"); ScalarStyle = scalarStyle; } } internal delegate IObjectGraphTraversalStrategy ObjectGraphTraversalStrategyFactory(ITypeInspector typeInspector, ITypeResolver typeResolver, IEnumerable typeConverters, int maximumRecursion); internal sealed class PropertyDescriptor : IPropertyDescriptor { private readonly IPropertyDescriptor baseDescriptor; public bool AllowNulls => baseDescriptor.AllowNulls; public string Name { get; set; } public bool Required => baseDescriptor.Required; public Type Type => baseDescriptor.Type; public Type? TypeOverride { get { return baseDescriptor.TypeOverride; } set { baseDescriptor.TypeOverride = value; } } public Type? ConverterType => baseDescriptor.ConverterType; public int Order { get; set; } public ScalarStyle ScalarStyle { get { return baseDescriptor.ScalarStyle; } set { baseDescriptor.ScalarStyle = value; } } public bool CanWrite => baseDescriptor.CanWrite; public PropertyDescriptor(IPropertyDescriptor baseDescriptor) { this.baseDescriptor = baseDescriptor; Name = baseDescriptor.Name; } public void Write(object target, object? value) { baseDescriptor.Write(target, value); } public T? GetCustomAttribute() where T : Attribute { return baseDescriptor.GetCustomAttribute(); } public IObjectDescriptor Read(object target) { return baseDescriptor.Read(target); } } internal sealed class Serializer : ISerializer { private readonly IValueSerializer valueSerializer; private readonly EmitterSettings emitterSettings; public Serializer() : this(new SerializerBuilder().BuildValueSerializer(), EmitterSettings.Default) { } private Serializer(IValueSerializer valueSerializer, EmitterSettings emitterSettings) { this.valueSerializer = valueSerializer ?? throw new ArgumentNullException("valueSerializer"); this.emitterSettings = emitterSettings ?? throw new ArgumentNullException("emitterSettings"); } public static Serializer FromValueSerializer(IValueSerializer valueSerializer, EmitterSettings emitterSettings) { return new Serializer(valueSerializer, emitterSettings); } public string Serialize(object? graph) { using StringWriter stringWriter = new StringWriter(); Serialize(stringWriter, graph); return stringWriter.ToString(); } public string Serialize(object? graph, Type type) { using StringWriter stringWriter = new StringWriter(); Serialize(stringWriter, graph, type); return stringWriter.ToString(); } public void Serialize(TextWriter writer, object? graph) { Serialize(new Emitter(writer, emitterSettings), graph); } public void Serialize(TextWriter writer, object? graph, Type type) { Serialize(new Emitter(writer, emitterSettings), graph, type); } public void Serialize(IEmitter emitter, object? graph) { if (emitter == null) { throw new ArgumentNullException("emitter"); } EmitDocument(emitter, graph, null); } public void Serialize(IEmitter emitter, object? graph, Type type) { if (emitter == null) { throw new ArgumentNullException("emitter"); } if (type == null) { throw new ArgumentNullException("type"); } EmitDocument(emitter, graph, type); } private void EmitDocument(IEmitter emitter, object? graph, Type? type) { emitter.Emit(new YamlDotNet.Core.Events.StreamStart()); emitter.Emit(new YamlDotNet.Core.Events.DocumentStart()); valueSerializer.SerializeValue(emitter, graph, type); emitter.Emit(new YamlDotNet.Core.Events.DocumentEnd(isImplicit: true)); emitter.Emit(new YamlDotNet.Core.Events.StreamEnd()); } } internal sealed class SerializerBuilder : BuilderSkeleton { private class ValueSerializer : IValueSerializer { private readonly IObjectGraphTraversalStrategy traversalStrategy; private readonly IEventEmitter eventEmitter; private readonly IEnumerable typeConverters; private readonly LazyComponentRegistrationList, IObjectGraphVisitor> preProcessingPhaseObjectGraphVisitorFactories; private readonly LazyComponentRegistrationList> emissionPhaseObjectGraphVisitorFactories; public ValueSerializer(IObjectGraphTraversalStrategy traversalStrategy, IEventEmitter eventEmitter, IEnumerable typeConverters, LazyComponentRegistrationList, IObjectGraphVisitor> preProcessingPhaseObjectGraphVisitorFactories, LazyComponentRegistrationList> emissionPhaseObjectGraphVisitorFactories) { this.traversalStrategy = traversalStrategy; this.eventEmitter = eventEmitter; this.typeConverters = typeConverters; this.preProcessingPhaseObjectGraphVisitorFactories = preProcessingPhaseObjectGraphVisitorFactories; this.emissionPhaseObjectGraphVisitorFactories = emissionPhaseObjectGraphVisitorFactories; } public void SerializeValue(IEmitter emitter, object? value, Type? type) { Type type2 = type ?? ((value != null) ? value.GetType() : typeof(object)); Type staticType = type ?? typeof(object); ObjectDescriptor graph = new ObjectDescriptor(value, type2, staticType); List> preProcessingPhaseObjectGraphVisitors = preProcessingPhaseObjectGraphVisitorFactories.BuildComponentList(typeConverters); IObjectGraphVisitor visitor = emissionPhaseObjectGraphVisitorFactories.BuildComponentChain>(new EmittingObjectGraphVisitor(eventEmitter), (IObjectGraphVisitor inner) => new EmissionPhaseObjectGraphVisitorArgs(inner, eventEmitter, preProcessingPhaseObjectGraphVisitors, typeConverters, NestedObjectSerializer)); foreach (IObjectGraphVisitor item in preProcessingPhaseObjectGraphVisitors) { traversalStrategy.Traverse(graph, item, default(Nothing), NestedObjectSerializer); } traversalStrategy.Traverse(graph, visitor, emitter, NestedObjectSerializer); void NestedObjectSerializer(object? v, Type? t) { SerializeValue(emitter, v, t); } } } private ObjectGraphTraversalStrategyFactory objectGraphTraversalStrategyFactory; private readonly LazyComponentRegistrationList, IObjectGraphVisitor> preProcessingPhaseObjectGraphVisitorFactories; private readonly LazyComponentRegistrationList> emissionPhaseObjectGraphVisitorFactories; private readonly LazyComponentRegistrationList eventEmitterFactories; private readonly Dictionary tagMappings = new Dictionary(); private readonly IObjectFactory objectFactory; private int maximumRecursion = 50; private EmitterSettings emitterSettings = EmitterSettings.Default; private DefaultValuesHandling defaultValuesHandlingConfiguration; private ScalarStyle defaultScalarStyle; private bool quoteNecessaryStrings; private bool quoteYaml1_1Strings; protected override SerializerBuilder Self => this; public SerializerBuilder() : base((ITypeResolver)new DynamicTypeResolver()) { typeInspectorFactories.Add(typeof(CachedTypeInspector), (ITypeInspector inner) => new CachedTypeInspector(inner)); typeInspectorFactories.Add(typeof(NamingConventionTypeInspector), (ITypeInspector inner) => (!(namingConvention is NullNamingConvention)) ? new NamingConventionTypeInspector(inner, namingConvention) : inner); typeInspectorFactories.Add(typeof(YamlAttributesTypeInspector), (ITypeInspector inner) => new YamlAttributesTypeInspector(inner)); typeInspectorFactories.Add(typeof(YamlAttributeOverridesInspector), (ITypeInspector inner) => (overrides == null) ? inner : new YamlAttributeOverridesInspector(inner, overrides.Clone())); preProcessingPhaseObjectGraphVisitorFactories = new LazyComponentRegistrationList, IObjectGraphVisitor> { { typeof(AnchorAssigner), (IEnumerable typeConverters) => new AnchorAssigner(typeConverters) } }; emissionPhaseObjectGraphVisitorFactories = new LazyComponentRegistrationList> { { typeof(CustomSerializationObjectGraphVisitor), (EmissionPhaseObjectGraphVisitorArgs args) => new CustomSerializationObjectGraphVisitor(args.InnerVisitor, args.TypeConverters, args.NestedObjectSerializer) }, { typeof(AnchorAssigningObjectGraphVisitor), (EmissionPhaseObjectGraphVisitorArgs args) => new AnchorAssigningObjectGraphVisitor(args.InnerVisitor, args.EventEmitter, args.GetPreProcessingPhaseObjectGraphVisitor()) }, { typeof(DefaultValuesObjectGraphVisitor), (EmissionPhaseObjectGraphVisitorArgs args) => new DefaultValuesObjectGraphVisitor(defaultValuesHandlingConfiguration, args.InnerVisitor, new DefaultObjectFactory()) }, { typeof(CommentsObjectGraphVisitor), (EmissionPhaseObjectGraphVisitorArgs args) => new CommentsObjectGraphVisitor(args.InnerVisitor) } }; eventEmitterFactories = new LazyComponentRegistrationList { { typeof(TypeAssigningEventEmitter), (IEventEmitter inner) => new TypeAssigningEventEmitter(inner, tagMappings, quoteNecessaryStrings, quoteYaml1_1Strings, defaultScalarStyle, yamlFormatter, enumNamingConvention, BuildTypeInspector()) } }; objectFactory = new DefaultObjectFactory(); objectGraphTraversalStrategyFactory = (ITypeInspector typeInspector, ITypeResolver typeResolver, IEnumerable typeConverters, int maximumRecursion) => new FullObjectGraphTraversalStrategy(typeInspector, typeResolver, maximumRecursion, namingConvention, objectFactory); } public SerializerBuilder WithQuotingNecessaryStrings(bool quoteYaml1_1Strings = false) { quoteNecessaryStrings = true; this.quoteYaml1_1Strings = quoteYaml1_1Strings; return this; } public SerializerBuilder WithDefaultScalarStyle(ScalarStyle style) { defaultScalarStyle = style; return this; } public SerializerBuilder WithMaximumRecursion(int maximumRecursion) { if (maximumRecursion <= 0) { throw new ArgumentOutOfRangeException("maximumRecursion", $"The maximum recursion specified ({maximumRecursion}) is invalid. It should be a positive integer."); } this.maximumRecursion = maximumRecursion; return this; } public SerializerBuilder WithEventEmitter(Func eventEmitterFactory) where TEventEmitter : IEventEmitter { return WithEventEmitter(eventEmitterFactory, delegate(IRegistrationLocationSelectionSyntax w) { w.OnTop(); }); } public SerializerBuilder WithEventEmitter(Func eventEmitterFactory) where TEventEmitter : IEventEmitter { return WithEventEmitter(eventEmitterFactory, delegate(IRegistrationLocationSelectionSyntax w) { w.OnTop(); }); } public SerializerBuilder WithEventEmitter(Func eventEmitterFactory, Action> where) where TEventEmitter : IEventEmitter { return WithEventEmitter((IEventEmitter e, ITypeInspector _) => eventEmitterFactory(e), where); } public SerializerBuilder WithEventEmitter(Func eventEmitterFactory, Action> where) where TEventEmitter : IEventEmitter { if (eventEmitterFactory == null) { throw new ArgumentNullException("eventEmitterFactory"); } if (where == null) { throw new ArgumentNullException("where"); } where(eventEmitterFactories.CreateRegistrationLocationSelector(typeof(TEventEmitter), (IEventEmitter inner) => eventEmitterFactory(inner, BuildTypeInspector()))); return Self; } public SerializerBuilder WithEventEmitter(WrapperFactory eventEmitterFactory, Action> where) where TEventEmitter : IEventEmitter { if (eventEmitterFactory == null) { throw new ArgumentNullException("eventEmitterFactory"); } if (where == null) { throw new ArgumentNullException("where"); } where(eventEmitterFactories.CreateTrackingRegistrationLocationSelector(typeof(TEventEmitter), (IEventEmitter wrapped, IEventEmitter inner) => eventEmitterFactory(wrapped, inner))); return Self; } public SerializerBuilder WithoutEventEmitter() where TEventEmitter : IEventEmitter { return WithoutEventEmitter(typeof(TEventEmitter)); } public SerializerBuilder WithoutEventEmitter(Type eventEmitterType) { if (eventEmitterType == null) { throw new ArgumentNullException("eventEmitterType"); } eventEmitterFactories.Remove(eventEmitterType); return this; } public override SerializerBuilder WithTagMapping(TagName tag, Type type) { if (tag.IsEmpty) { throw new ArgumentException("Non-specific tags cannot be maped"); } if (type == null) { throw new ArgumentNullException("type"); } if (tagMappings.TryGetValue(type, out var value)) { throw new ArgumentException($"Type already has a registered tag '{value}' for type '{type.FullName}'", "type"); } tagMappings.Add(type, tag); return this; } public SerializerBuilder WithoutTagMapping(Type type) { if (type == null) { throw new ArgumentNullException("type"); } if (!tagMappings.Remove(type)) { throw new KeyNotFoundException("Tag for type '" + type.FullName + "' is not registered"); } return this; } public SerializerBuilder EnsureRoundtrip() { objectGraphTraversalStrategyFactory = (ITypeInspector typeInspector, ITypeResolver typeResolver, IEnumerable typeConverters, int maximumRecursion) => new RoundtripObjectGraphTraversalStrategy(typeConverters, typeInspector, typeResolver, maximumRecursion, namingConvention, settings, objectFactory); WithEventEmitter((IEventEmitter inner) => new TypeAssigningEventEmitter(inner, tagMappings, quoteNecessaryStrings, quoteYaml1_1Strings, defaultScalarStyle, yamlFormatter, enumNamingConvention, BuildTypeInspector()), delegate(IRegistrationLocationSelectionSyntax loc) { loc.InsteadOf(); }); return WithTypeInspector((ITypeInspector inner) => new ReadableAndWritablePropertiesTypeInspector(inner), delegate(IRegistrationLocationSelectionSyntax loc) { loc.OnBottom(); }); } public SerializerBuilder DisableAliases() { preProcessingPhaseObjectGraphVisitorFactories.Remove(typeof(AnchorAssigner)); emissionPhaseObjectGraphVisitorFactories.Remove(typeof(AnchorAssigningObjectGraphVisitor)); return this; } [Obsolete("The default behavior is now to always emit default values, thefore calling this method has no effect. This behavior is now controlled by ConfigureDefaultValuesHandling.", true)] public SerializerBuilder EmitDefaults() { return ConfigureDefaultValuesHandling(DefaultValuesHandling.Preserve); } public SerializerBuilder ConfigureDefaultValuesHandling(DefaultValuesHandling configuration) { defaultValuesHandlingConfiguration = configuration; return this; } public SerializerBuilder JsonCompatible() { emitterSettings = emitterSettings.WithMaxSimpleKeyLength(int.MaxValue).WithoutAnchorName().WithUtf16SurrogatePairs(); return WithTypeConverter(new YamlDotNet.Serialization.Converters.GuidConverter(jsonCompatible: true), delegate(IRegistrationLocationSelectionSyntax w) { w.InsteadOf(); }).WithTypeConverter(new DateTime8601Converter(ScalarStyle.DoubleQuoted)).WithEventEmitter((IEventEmitter inner) => new JsonEventEmitter(inner, yamlFormatter, enumNamingConvention, BuildTypeInspector()), delegate(IRegistrationLocationSelectionSyntax loc) { loc.InsteadOf(); }); } public SerializerBuilder WithNewLine(string newLine) { emitterSettings = emitterSettings.WithNewLine(newLine); return this; } public SerializerBuilder WithPreProcessingPhaseObjectGraphVisitor(TObjectGraphVisitor objectGraphVisitor) where TObjectGraphVisitor : IObjectGraphVisitor { return WithPreProcessingPhaseObjectGraphVisitor(objectGraphVisitor, delegate(IRegistrationLocationSelectionSyntax> w) { w.OnTop(); }); } public SerializerBuilder WithPreProcessingPhaseObjectGraphVisitor(Func, TObjectGraphVisitor> objectGraphVisitorFactory) where TObjectGraphVisitor : IObjectGraphVisitor { return WithPreProcessingPhaseObjectGraphVisitor(objectGraphVisitorFactory, delegate(IRegistrationLocationSelectionSyntax> w) { w.OnTop(); }); } public SerializerBuilder WithPreProcessingPhaseObjectGraphVisitor(TObjectGraphVisitor objectGraphVisitor, Action>> where) where TObjectGraphVisitor : IObjectGraphVisitor { if (objectGraphVisitor == null) { throw new ArgumentNullException("objectGraphVisitor"); } if (where == null) { throw new ArgumentNullException("where"); } where(preProcessingPhaseObjectGraphVisitorFactories.CreateRegistrationLocationSelector(typeof(TObjectGraphVisitor), (IEnumerable _) => objectGraphVisitor)); return this; } public SerializerBuilder WithPreProcessingPhaseObjectGraphVisitor(Func, TObjectGraphVisitor> objectGraphVisitorFactory, Action>> where) where TObjectGraphVisitor : IObjectGraphVisitor { if (objectGraphVisitorFactory == null) { throw new ArgumentNullException("objectGraphVisitorFactory"); } if (where == null) { throw new ArgumentNullException("where"); } where(preProcessingPhaseObjectGraphVisitorFactories.CreateRegistrationLocationSelector(typeof(TObjectGraphVisitor), (IEnumerable typeConverters) => objectGraphVisitorFactory(typeConverters))); return this; } public SerializerBuilder WithPreProcessingPhaseObjectGraphVisitor(WrapperFactory, TObjectGraphVisitor> objectGraphVisitorFactory, Action>> where) where TObjectGraphVisitor : IObjectGraphVisitor { if (objectGraphVisitorFactory == null) { throw new ArgumentNullException("objectGraphVisitorFactory"); } if (where == null) { throw new ArgumentNullException("where"); } where(preProcessingPhaseObjectGraphVisitorFactories.CreateTrackingRegistrationLocationSelector(typeof(TObjectGraphVisitor), (IObjectGraphVisitor wrapped, IEnumerable _) => objectGraphVisitorFactory(wrapped))); return this; } public SerializerBuilder WithPreProcessingPhaseObjectGraphVisitor(WrapperFactory, IObjectGraphVisitor, TObjectGraphVisitor> objectGraphVisitorFactory, Action>> where) where TObjectGraphVisitor : IObjectGraphVisitor { if (objectGraphVisitorFactory == null) { throw new ArgumentNullException("objectGraphVisitorFactory"); } if (where == null) { throw new ArgumentNullException("where"); } where(preProcessingPhaseObjectGraphVisitorFactories.CreateTrackingRegistrationLocationSelector(typeof(TObjectGraphVisitor), (IObjectGraphVisitor wrapped, IEnumerable typeConverters) => objectGraphVisitorFactory(wrapped, typeConverters))); return this; } public SerializerBuilder WithoutPreProcessingPhaseObjectGraphVisitor() where TObjectGraphVisitor : IObjectGraphVisitor { return WithoutPreProcessingPhaseObjectGraphVisitor(typeof(TObjectGraphVisitor)); } public SerializerBuilder WithoutPreProcessingPhaseObjectGraphVisitor(Type objectGraphVisitorType) { if (objectGraphVisitorType == null) { throw new ArgumentNullException("objectGraphVisitorType"); } preProcessingPhaseObjectGraphVisitorFactories.Remove(objectGraphVisitorType); return this; } public SerializerBuilder WithObjectGraphTraversalStrategyFactory(ObjectGraphTraversalStrategyFactory objectGraphTraversalStrategyFactory) { this.objectGraphTraversalStrategyFactory = objectGraphTraversalStrategyFactory; return this; } public SerializerBuilder WithEmissionPhaseObjectGraphVisitor(Func objectGraphVisitorFactory) where TObjectGraphVisitor : IObjectGraphVisitor { return WithEmissionPhaseObjectGraphVisitor(objectGraphVisitorFactory, delegate(IRegistrationLocationSelectionSyntax> w) { w.OnTop(); }); } public SerializerBuilder WithEmissionPhaseObjectGraphVisitor(Func objectGraphVisitorFactory, Action>> where) where TObjectGraphVisitor : IObjectGraphVisitor { if (objectGraphVisitorFactory == null) { throw new ArgumentNullException("objectGraphVisitorFactory"); } if (where == null) { throw new ArgumentNullException("where"); } where(emissionPhaseObjectGraphVisitorFactories.CreateRegistrationLocationSelector(typeof(TObjectGraphVisitor), (EmissionPhaseObjectGraphVisitorArgs args) => objectGraphVisitorFactory(args))); return this; } public SerializerBuilder WithEmissionPhaseObjectGraphVisitor(WrapperFactory, TObjectGraphVisitor> objectGraphVisitorFactory, Action>> where) where TObjectGraphVisitor : IObjectGraphVisitor { if (objectGraphVisitorFactory == null) { throw new ArgumentNullException("objectGraphVisitorFactory"); } if (where == null) { throw new ArgumentNullException("where"); } where(emissionPhaseObjectGraphVisitorFactories.CreateTrackingRegistrationLocationSelector(typeof(TObjectGraphVisitor), (IObjectGraphVisitor wrapped, EmissionPhaseObjectGraphVisitorArgs args) => objectGraphVisitorFactory(wrapped, args))); return this; } public SerializerBuilder WithoutEmissionPhaseObjectGraphVisitor() where TObjectGraphVisitor : IObjectGraphVisitor { return WithoutEmissionPhaseObjectGraphVisitor(typeof(TObjectGraphVisitor)); } public SerializerBuilder WithoutEmissionPhaseObjectGraphVisitor(Type objectGraphVisitorType) { if (objectGraphVisitorType == null) { throw new ArgumentNullException("objectGraphVisitorType"); } emissionPhaseObjectGraphVisitorFactories.Remove(objectGraphVisitorType); return this; } public SerializerBuilder WithIndentedSequences() { emitterSettings = emitterSettings.WithIndentedSequences(); return this; } public ISerializer Build() { if (FsharpHelper.Instance == null) { FsharpHelper.Instance = new DefaultFsharpHelper(); } return Serializer.FromValueSerializer(BuildValueSerializer(), emitterSettings); } public IValueSerializer BuildValueSerializer() { IEnumerable typeConverters = BuildTypeConverters(); ITypeInspector typeInspector = BuildTypeInspector(); IObjectGraphTraversalStrategy traversalStrategy = objectGraphTraversalStrategyFactory(typeInspector, typeResolver, typeConverters, maximumRecursion); IEventEmitter eventEmitter = eventEmitterFactories.BuildComponentChain(new WriterEventEmitter()); return new ValueSerializer(traversalStrategy, eventEmitter, typeConverters, preProcessingPhaseObjectGraphVisitorFactories.Clone(), emissionPhaseObjectGraphVisitorFactories.Clone()); } public ITypeInspector BuildTypeInspector() { ITypeInspector typeInspector = new ReadablePropertiesTypeInspector(typeResolver, includeNonPublicProperties); if (!ignoreFields) { typeInspector = new CompositeTypeInspector(new ReadableFieldsTypeInspector(typeResolver), typeInspector); } return typeInspectorFactories.BuildComponentChain(typeInspector); } } internal class Settings { public bool AllowPrivateConstructors { get; set; } } internal abstract class StaticBuilderSkeleton where TBuilder : StaticBuilderSkeleton { internal INamingConvention namingConvention = NullNamingConvention.Instance; internal INamingConvention enumNamingConvention = NullNamingConvention.Instance; internal ITypeResolver typeResolver; internal readonly LazyComponentRegistrationList typeConverterFactories; internal readonly LazyComponentRegistrationList typeInspectorFactories; internal bool includeNonPublicProperties; internal Settings settings; internal YamlFormatter yamlFormatter = YamlFormatter.Default; protected abstract TBuilder Self { get; } internal StaticBuilderSkeleton(ITypeResolver typeResolver) { typeConverterFactories = new LazyComponentRegistrationList { { typeof(YamlDotNet.Serialization.Converters.GuidConverter), (Nothing _) => new YamlDotNet.Serialization.Converters.GuidConverter(jsonCompatible: false) } }; typeInspectorFactories = new LazyComponentRegistrationList(); this.typeResolver = typeResolver ?? throw new ArgumentNullException("typeResolver"); settings = new Settings(); } public TBuilder WithNamingConvention(INamingConvention namingConvention) { this.namingConvention = namingConvention ?? throw new ArgumentNullException("namingConvention"); return Self; } public TBuilder WithEnumNamingConvention(INamingConvention enumNamingConvention) { this.enumNamingConvention = enumNamingConvention ?? throw new ArgumentNullException("enumNamingConvention"); return Self; } public TBuilder WithTypeResolver(ITypeResolver typeResolver) { this.typeResolver = typeResolver ?? throw new ArgumentNullException("typeResolver"); return Self; } public abstract TBuilder WithTagMapping(TagName tag, Type type); public TBuilder WithTypeConverter(IYamlTypeConverter typeConverter) { return WithTypeConverter(typeConverter, delegate(IRegistrationLocationSelectionSyntax w) { w.OnTop(); }); } public TBuilder WithTypeConverter(IYamlTypeConverter typeConverter, Action> where) { if (typeConverter == null) { throw new ArgumentNullException("typeConverter"); } if (where == null) { throw new ArgumentNullException("where"); } where(typeConverterFactories.CreateRegistrationLocationSelector(typeConverter.GetType(), (Nothing _) => typeConverter)); return Self; } public TBuilder WithTypeConverter(WrapperFactory typeConverterFactory, Action> where) where TYamlTypeConverter : IYamlTypeConverter { if (typeConverterFactory == null) { throw new ArgumentNullException("typeConverterFactory"); } if (where == null) { throw new ArgumentNullException("where"); } where(typeConverterFactories.CreateTrackingRegistrationLocationSelector(typeof(TYamlTypeConverter), (IYamlTypeConverter wrapped, Nothing _) => typeConverterFactory(wrapped))); return Self; } public TBuilder WithoutTypeConverter() where TYamlTypeConverter : IYamlTypeConverter { return WithoutTypeConverter(typeof(TYamlTypeConverter)); } public TBuilder WithoutTypeConverter(Type converterType) { if (converterType == null) { throw new ArgumentNullException("converterType"); } typeConverterFactories.Remove(converterType); return Self; } public TBuilder WithTypeInspector(Func typeInspectorFactory) where TTypeInspector : ITypeInspector { return WithTypeInspector(typeInspectorFactory, delegate(IRegistrationLocationSelectionSyntax w) { w.OnTop(); }); } public TBuilder WithTypeInspector(Func typeInspectorFactory, Action> where) where TTypeInspector : ITypeInspector { if (typeInspectorFactory == null) { throw new ArgumentNullException("typeInspectorFactory"); } if (where == null) { throw new ArgumentNullException("where"); } where(typeInspectorFactories.CreateRegistrationLocationSelector(typeof(TTypeInspector), (ITypeInspector inner) => typeInspectorFactory(inner))); return Self; } public TBuilder WithTypeInspector(WrapperFactory typeInspectorFactory, Action> where) where TTypeInspector : ITypeInspector { if (typeInspectorFactory == null) { throw new ArgumentNullException("typeInspectorFactory"); } if (where == null) { throw new ArgumentNullException("where"); } where(typeInspectorFactories.CreateTrackingRegistrationLocationSelector(typeof(TTypeInspector), (ITypeInspector wrapped, ITypeInspector inner) => typeInspectorFactory(wrapped, inner))); return Self; } public TBuilder WithoutTypeInspector() where TTypeInspector : ITypeInspector { return WithoutTypeInspector(typeof(TTypeInspector)); } public TBuilder WithoutTypeInspector(Type inspectorType) { if (inspectorType == null) { throw new ArgumentNullException("inspectorType"); } typeInspectorFactories.Remove(inspectorType); return Self; } public TBuilder WithYamlFormatter(YamlFormatter formatter) { yamlFormatter = formatter ?? throw new ArgumentNullException("formatter"); return Self; } protected IEnumerable BuildTypeConverters() { return typeConverterFactories.BuildComponentList(); } } internal abstract class StaticContext { public virtual bool IsKnownType(Type type) { throw new NotImplementedException(); } public virtual ITypeResolver GetTypeResolver() { throw new NotImplementedException(); } public virtual StaticObjectFactory GetFactory() { throw new NotImplementedException(); } public virtual ITypeInspector GetTypeInspector() { throw new NotImplementedException(); } } internal sealed class StaticDeserializerBuilder : StaticBuilderSkeleton { private readonly StaticContext context; private readonly StaticObjectFactory factory; private readonly LazyComponentRegistrationList nodeDeserializerFactories; private readonly LazyComponentRegistrationList nodeTypeResolverFactories; private readonly Dictionary tagMappings; private readonly ITypeConverter typeConverter; private readonly Dictionary typeMappings; private bool ignoreUnmatched; private bool duplicateKeyChecking; private bool attemptUnknownTypeDeserialization; private bool enforceNullability; private bool caseInsensitivePropertyMatching; protected override StaticDeserializerBuilder Self => this; public StaticDeserializerBuilder(StaticContext context) : base(context.GetTypeResolver()) { this.context = context; factory = context.GetFactory(); typeMappings = new Dictionary(); tagMappings = new Dictionary { { FailsafeSchema.Tags.Map, typeof(Dictionary) }, { FailsafeSchema.Tags.Str, typeof(string) }, { JsonSchema.Tags.Bool, typeof(bool) }, { JsonSchema.Tags.Float, typeof(double) }, { JsonSchema.Tags.Int, typeof(int) }, { DefaultSchema.Tags.Timestamp, typeof(DateTime) } }; typeInspectorFactories.Add(typeof(CachedTypeInspector), (ITypeInspector inner) => new CachedTypeInspector(inner)); typeInspectorFactories.Add(typeof(NamingConventionTypeInspector), (ITypeInspector inner) => (!(namingConvention is NullNamingConvention)) ? new NamingConventionTypeInspector(inner, namingConvention) : inner); typeInspectorFactories.Add(typeof(YamlAttributesTypeInspector), (ITypeInspector inner) => new YamlAttributesTypeInspector(inner)); nodeDeserializerFactories = new LazyComponentRegistrationList { { typeof(YamlConvertibleNodeDeserializer), (Nothing _) => new YamlConvertibleNodeDeserializer(factory) }, { typeof(YamlSerializableNodeDeserializer), (Nothing _) => new YamlSerializableNodeDeserializer(factory) }, { typeof(TypeConverterNodeDeserializer), (Nothing _) => new TypeConverterNodeDeserializer(BuildTypeConverters()) }, { typeof(NullNodeDeserializer), (Nothing _) => new NullNodeDeserializer() }, { typeof(ScalarNodeDeserializer), (Nothing _) => new ScalarNodeDeserializer(attemptUnknownTypeDeserialization, typeConverter, BuildTypeInspector(), yamlFormatter, enumNamingConvention) }, { typeof(StaticArrayNodeDeserializer), (Nothing _) => new StaticArrayNodeDeserializer(factory) }, { typeof(StaticDictionaryNodeDeserializer), (Nothing _) => new StaticDictionaryNodeDeserializer(factory, duplicateKeyChecking) }, { typeof(StaticCollectionNodeDeserializer), (Nothing _) => new StaticCollectionNodeDeserializer(factory) }, { typeof(ObjectNodeDeserializer), (Nothing _) => new ObjectNodeDeserializer(factory, BuildTypeInspector(), ignoreUnmatched, duplicateKeyChecking, typeConverter, enumNamingConvention, enforceNullability, caseInsensitivePropertyMatching, enforceRequiredProperties: false, BuildTypeConverters()) } }; nodeTypeResolverFactories = new LazyComponentRegistrationList { { typeof(MappingNodeTypeResolver), (Nothing _) => new MappingNodeTypeResolver(typeMappings) }, { typeof(YamlConvertibleTypeResolver), (Nothing _) => new YamlConvertibleTypeResolver() }, { typeof(YamlSerializableTypeResolver), (Nothing _) => new YamlSerializableTypeResolver() }, { typeof(TagNodeTypeResolver), (Nothing _) => new TagNodeTypeResolver(tagMappings) }, { typeof(PreventUnknownTagsNodeTypeResolver), (Nothing _) => new PreventUnknownTagsNodeTypeResolver() }, { typeof(DefaultContainersNodeTypeResolver), (Nothing _) => new DefaultContainersNodeTypeResolver() } }; typeConverter = new NullTypeConverter(); } public ITypeInspector BuildTypeInspector() { ITypeInspector typeInspector = context.GetTypeInspector(); return typeInspectorFactories.BuildComponentChain(typeInspector); } public StaticDeserializerBuilder WithAttemptingUnquotedStringTypeDeserialization() { attemptUnknownTypeDeserialization = true; return this; } public StaticDeserializerBuilder WithNodeDeserializer(INodeDeserializer nodeDeserializer) { return WithNodeDeserializer(nodeDeserializer, delegate(IRegistrationLocationSelectionSyntax w) { w.OnTop(); }); } public StaticDeserializerBuilder WithNodeDeserializer(INodeDeserializer nodeDeserializer, Action> where) { if (nodeDeserializer == null) { throw new ArgumentNullException("nodeDeserializer"); } if (where == null) { throw new ArgumentNullException("where"); } where(nodeDeserializerFactories.CreateRegistrationLocationSelector(nodeDeserializer.GetType(), (Nothing _) => nodeDeserializer)); return this; } public StaticDeserializerBuilder WithNodeDeserializer(WrapperFactory nodeDeserializerFactory, Action> where) where TNodeDeserializer : INodeDeserializer { if (nodeDeserializerFactory == null) { throw new ArgumentNullException("nodeDeserializerFactory"); } if (where == null) { throw new ArgumentNullException("where"); } where(nodeDeserializerFactories.CreateTrackingRegistrationLocationSelector(typeof(TNodeDeserializer), (INodeDeserializer wrapped, Nothing _) => nodeDeserializerFactory(wrapped))); return this; } public StaticDeserializerBuilder WithCaseInsensitivePropertyMatching() { caseInsensitivePropertyMatching = true; return this; } public StaticDeserializerBuilder WithEnforceNullability() { enforceNullability = true; return this; } public StaticDeserializerBuilder WithoutNodeDeserializer() where TNodeDeserializer : INodeDeserializer { return WithoutNodeDeserializer(typeof(TNodeDeserializer)); } public StaticDeserializerBuilder WithoutNodeDeserializer(Type nodeDeserializerType) { if (nodeDeserializerType == null) { throw new ArgumentNullException("nodeDeserializerType"); } nodeDeserializerFactories.Remove(nodeDeserializerType); return this; } public StaticDeserializerBuilder WithTypeDiscriminatingNodeDeserializer(Action configureTypeDiscriminatingNodeDeserializerOptions, int maxDepth = -1, int maxLength = -1) { TypeDiscriminatingNodeDeserializerOptions typeDiscriminatingNodeDeserializerOptions = new TypeDiscriminatingNodeDeserializerOptions(); configureTypeDiscriminatingNodeDeserializerOptions(typeDiscriminatingNodeDeserializerOptions); TypeDiscriminatingNodeDeserializer nodeDeserializer = new TypeDiscriminatingNodeDeserializer(nodeDeserializerFactories.BuildComponentList(), typeDiscriminatingNodeDeserializerOptions.discriminators, maxDepth, maxLength); return WithNodeDeserializer(nodeDeserializer, delegate(IRegistrationLocationSelectionSyntax s) { s.Before(); }); } public StaticDeserializerBuilder WithNodeTypeResolver(INodeTypeResolver nodeTypeResolver) { return WithNodeTypeResolver(nodeTypeResolver, delegate(IRegistrationLocationSelectionSyntax w) { w.OnTop(); }); } public StaticDeserializerBuilder WithNodeTypeResolver(INodeTypeResolver nodeTypeResolver, Action> where) { if (nodeTypeResolver == null) { throw new ArgumentNullException("nodeTypeResolver"); } if (where == null) { throw new ArgumentNullException("where"); } where(nodeTypeResolverFactories.CreateRegistrationLocationSelector(nodeTypeResolver.GetType(), (Nothing _) => nodeTypeResolver)); return this; } public StaticDeserializerBuilder WithNodeTypeResolver(WrapperFactory nodeTypeResolverFactory, Action> where) where TNodeTypeResolver : INodeTypeResolver { if (nodeTypeResolverFactory == null) { throw new ArgumentNullException("nodeTypeResolverFactory"); } if (where == null) { throw new ArgumentNullException("where"); } where(nodeTypeResolverFactories.CreateTrackingRegistrationLocationSelector(typeof(TNodeTypeResolver), (INodeTypeResolver wrapped, Nothing _) => nodeTypeResolverFactory(wrapped))); return this; } public StaticDeserializerBuilder WithoutNodeTypeResolver() where TNodeTypeResolver : INodeTypeResolver { return WithoutNodeTypeResolver(typeof(TNodeTypeResolver)); } public StaticDeserializerBuilder WithoutNodeTypeResolver(Type nodeTypeResolverType) { if (nodeTypeResolverType == null) { throw new ArgumentNullException("nodeTypeResolverType"); } nodeTypeResolverFactories.Remove(nodeTypeResolverType); return this; } public override StaticDeserializerBuilder WithTagMapping(TagName tag, Type type) { if (tag.IsEmpty) { throw new ArgumentException("Non-specific tags cannot be maped"); } if (type == null) { throw new ArgumentNullException("type"); } if (tagMappings.TryGetValue(tag, out Type value)) { throw new ArgumentException($"Type already has a registered type '{value.FullName}' for tag '{tag}'", "tag"); } tagMappings.Add(tag, type); return this; } public StaticDeserializerBuilder WithTypeMapping() where TConcrete : TInterface { Type typeFromHandle = typeof(TInterface); Type typeFromHandle2 = typeof(TConcrete); if (!typeFromHandle.IsAssignableFrom(typeFromHandle2)) { throw new InvalidOperationException("The type '" + typeFromHandle2.Name + "' does not implement interface '" + typeFromHandle.Name + "'."); } typeMappings[typeFromHandle] = typeFromHandle2; return this; } public StaticDeserializerBuilder WithoutTagMapping(TagName tag) { if (tag.IsEmpty) { throw new ArgumentException("Non-specific tags cannot be maped"); } if (!tagMappings.Remove(tag)) { throw new KeyNotFoundException($"Tag '{tag}' is not registered"); } return this; } public StaticDeserializerBuilder IgnoreUnmatchedProperties() { ignoreUnmatched = true; return this; } public StaticDeserializerBuilder WithDuplicateKeyChecking() { duplicateKeyChecking = true; return this; } public IDeserializer Build() { return Deserializer.FromValueDeserializer(BuildValueDeserializer()); } public IValueDeserializer BuildValueDeserializer() { return new AliasValueDeserializer(new NodeValueDeserializer(nodeDeserializerFactories.BuildComponentList(), nodeTypeResolverFactories.BuildComponentList(), typeConverter, enumNamingConvention, BuildTypeInspector())); } } internal sealed class StaticSerializerBuilder : StaticBuilderSkeleton { private class ValueSerializer : IValueSerializer { private readonly IObjectGraphTraversalStrategy traversalStrategy; private readonly IEventEmitter eventEmitter; private readonly IEnumerable typeConverters; private readonly LazyComponentRegistrationList, IObjectGraphVisitor> preProcessingPhaseObjectGraphVisitorFactories; private readonly LazyComponentRegistrationList> emissionPhaseObjectGraphVisitorFactories; public ValueSerializer(IObjectGraphTraversalStrategy traversalStrategy, IEventEmitter eventEmitter, IEnumerable typeConverters, LazyComponentRegistrationList, IObjectGraphVisitor> preProcessingPhaseObjectGraphVisitorFactories, LazyComponentRegistrationList> emissionPhaseObjectGraphVisitorFactories) { this.traversalStrategy = traversalStrategy; this.eventEmitter = eventEmitter; this.typeConverters = typeConverters; this.preProcessingPhaseObjectGraphVisitorFactories = preProcessingPhaseObjectGraphVisitorFactories; this.emissionPhaseObjectGraphVisitorFactories = emissionPhaseObjectGraphVisitorFactories; } public void SerializeValue(IEmitter emitter, object? value, Type? type) { Type type2 = type ?? ((value != null) ? value.GetType() : typeof(object)); Type staticType = type ?? typeof(object); ObjectDescriptor graph = new ObjectDescriptor(value, type2, staticType); List> preProcessingPhaseObjectGraphVisitors = preProcessingPhaseObjectGraphVisitorFactories.BuildComponentList(typeConverters); foreach (IObjectGraphVisitor item in preProcessingPhaseObjectGraphVisitors) { traversalStrategy.Traverse(graph, item, default(Nothing), NestedObjectSerializer); } IObjectGraphVisitor visitor = emissionPhaseObjectGraphVisitorFactories.BuildComponentChain>(new EmittingObjectGraphVisitor(eventEmitter), (IObjectGraphVisitor inner) => new EmissionPhaseObjectGraphVisitorArgs(inner, eventEmitter, preProcessingPhaseObjectGraphVisitors, typeConverters, NestedObjectSerializer)); traversalStrategy.Traverse(graph, visitor, emitter, NestedObjectSerializer); void NestedObjectSerializer(object? v, Type? t) { SerializeValue(emitter, v, t); } } } private readonly StaticContext context; private readonly StaticObjectFactory factory; private ObjectGraphTraversalStrategyFactory objectGraphTraversalStrategyFactory; private readonly LazyComponentRegistrationList, IObjectGraphVisitor> preProcessingPhaseObjectGraphVisitorFactories; private readonly LazyComponentRegistrationList> emissionPhaseObjectGraphVisitorFactories; private readonly LazyComponentRegistrationList eventEmitterFactories; private readonly Dictionary tagMappings = new Dictionary(); private int maximumRecursion = 50; private EmitterSettings emitterSettings = EmitterSettings.Default; private DefaultValuesHandling defaultValuesHandlingConfiguration; private bool quoteNecessaryStrings; private bool quoteYaml1_1Strings; private ScalarStyle defaultScalarStyle; protected override StaticSerializerBuilder Self => this; public StaticSerializerBuilder(StaticContext context) : base((ITypeResolver)new DynamicTypeResolver()) { this.context = context; factory = context.GetFactory(); typeInspectorFactories.Add(typeof(CachedTypeInspector), (ITypeInspector inner) => new CachedTypeInspector(inner)); typeInspectorFactories.Add(typeof(NamingConventionTypeInspector), (ITypeInspector inner) => (!(namingConvention is NullNamingConvention)) ? new NamingConventionTypeInspector(inner, namingConvention) : inner); typeInspectorFactories.Add(typeof(YamlAttributesTypeInspector), (ITypeInspector inner) => new YamlAttributesTypeInspector(inner)); preProcessingPhaseObjectGraphVisitorFactories = new LazyComponentRegistrationList, IObjectGraphVisitor> { { typeof(AnchorAssigner), (IEnumerable typeConverters) => new AnchorAssigner(typeConverters) } }; emissionPhaseObjectGraphVisitorFactories = new LazyComponentRegistrationList> { { typeof(CustomSerializationObjectGraphVisitor), (EmissionPhaseObjectGraphVisitorArgs args) => new CustomSerializationObjectGraphVisitor(args.InnerVisitor, args.TypeConverters, args.NestedObjectSerializer) }, { typeof(AnchorAssigningObjectGraphVisitor), (EmissionPhaseObjectGraphVisitorArgs args) => new AnchorAssigningObjectGraphVisitor(args.InnerVisitor, args.EventEmitter, args.GetPreProcessingPhaseObjectGraphVisitor()) }, { typeof(DefaultValuesObjectGraphVisitor), (EmissionPhaseObjectGraphVisitorArgs args) => new DefaultValuesObjectGraphVisitor(defaultValuesHandlingConfiguration, args.InnerVisitor, factory) }, { typeof(CommentsObjectGraphVisitor), (EmissionPhaseObjectGraphVisitorArgs args) => new CommentsObjectGraphVisitor(args.InnerVisitor) } }; eventEmitterFactories = new LazyComponentRegistrationList { { typeof(TypeAssigningEventEmitter), (IEventEmitter inner) => new TypeAssigningEventEmitter(inner, tagMappings, quoteNecessaryStrings, quoteYaml1_1Strings, defaultScalarStyle, yamlFormatter, enumNamingConvention, BuildTypeInspector()) } }; objectGraphTraversalStrategyFactory = (ITypeInspector typeInspector, ITypeResolver typeResolver, IEnumerable typeConverters, int maximumRecursion) => new FullObjectGraphTraversalStrategy(typeInspector, typeResolver, maximumRecursion, namingConvention, factory); } public StaticSerializerBuilder WithQuotingNecessaryStrings(bool quoteYaml1_1Strings = false) { quoteNecessaryStrings = true; this.quoteYaml1_1Strings = quoteYaml1_1Strings; return this; } public StaticSerializerBuilder WithQuotingNecessaryStrings() { quoteNecessaryStrings = true; return this; } public StaticSerializerBuilder WithDefaultScalarStyle(ScalarStyle style) { defaultScalarStyle = style; return this; } public StaticSerializerBuilder WithMaximumRecursion(int maximumRecursion) { if (maximumRecursion <= 0) { throw new ArgumentOutOfRangeException("maximumRecursion", $"The maximum recursion specified ({maximumRecursion}) is invalid. It should be a positive integer."); } this.maximumRecursion = maximumRecursion; return this; } public StaticSerializerBuilder WithEventEmitter(Func eventEmitterFactory) where TEventEmitter : IEventEmitter { return WithEventEmitter(eventEmitterFactory, delegate(IRegistrationLocationSelectionSyntax w) { w.OnTop(); }); } public StaticSerializerBuilder WithEventEmitter(Func eventEmitterFactory) where TEventEmitter : IEventEmitter { return WithEventEmitter(eventEmitterFactory, delegate(IRegistrationLocationSelectionSyntax w) { w.OnTop(); }); } public StaticSerializerBuilder WithEventEmitter(Func eventEmitterFactory, Action> where) where TEventEmitter : IEventEmitter { return WithEventEmitter((IEventEmitter e, ITypeInspector _) => eventEmitterFactory(e), where); } public StaticSerializerBuilder WithEventEmitter(Func eventEmitterFactory, Action> where) where TEventEmitter : IEventEmitter { if (eventEmitterFactory == null) { throw new ArgumentNullException("eventEmitterFactory"); } if (where == null) { throw new ArgumentNullException("where"); } where(eventEmitterFactories.CreateRegistrationLocationSelector(typeof(TEventEmitter), (IEventEmitter inner) => eventEmitterFactory(inner, BuildTypeInspector()))); return Self; } public StaticSerializerBuilder WithEventEmitter(WrapperFactory eventEmitterFactory, Action> where) where TEventEmitter : IEventEmitter { if (eventEmitterFactory == null) { throw new ArgumentNullException("eventEmitterFactory"); } if (where == null) { throw new ArgumentNullException("where"); } where(eventEmitterFactories.CreateTrackingRegistrationLocationSelector(typeof(TEventEmitter), (IEventEmitter wrapped, IEventEmitter inner) => eventEmitterFactory(wrapped, inner))); return Self; } public StaticSerializerBuilder WithoutEventEmitter() where TEventEmitter : IEventEmitter { return WithoutEventEmitter(typeof(TEventEmitter)); } public StaticSerializerBuilder WithoutEventEmitter(Type eventEmitterType) { if (eventEmitterType == null) { throw new ArgumentNullException("eventEmitterType"); } eventEmitterFactories.Remove(eventEmitterType); return this; } public override StaticSerializerBuilder WithTagMapping(TagName tag, Type type) { if (tag.IsEmpty) { throw new ArgumentException("Non-specific tags cannot be maped"); } if (type == null) { throw new ArgumentNullException("type"); } if (tagMappings.TryGetValue(type, out var value)) { throw new ArgumentException($"Type already has a registered tag '{value}' for type '{type.FullName}'", "type"); } tagMappings.Add(type, tag); return this; } public StaticSerializerBuilder WithoutTagMapping(Type type) { if (type == null) { throw new ArgumentNullException("type"); } if (!tagMappings.Remove(type)) { throw new KeyNotFoundException("Tag for type '" + type.FullName + "' is not registered"); } return this; } public StaticSerializerBuilder EnsureRoundtrip() { objectGraphTraversalStrategyFactory = (ITypeInspector typeInspector, ITypeResolver typeResolver, IEnumerable typeConverters, int maximumRecursion) => new RoundtripObjectGraphTraversalStrategy(typeConverters, typeInspector, typeResolver, maximumRecursion, namingConvention, settings, factory); WithEventEmitter((IEventEmitter inner) => new TypeAssigningEventEmitter(inner, tagMappings, quoteNecessaryStrings, quoteYaml1_1Strings: false, ScalarStyle.Plain, YamlFormatter.Default, enumNamingConvention, BuildTypeInspector()), delegate(IRegistrationLocationSelectionSyntax loc) { loc.InsteadOf(); }); return WithTypeInspector((ITypeInspector inner) => new ReadableAndWritablePropertiesTypeInspector(inner), delegate(IRegistrationLocationSelectionSyntax loc) { loc.OnBottom(); }); } public StaticSerializerBuilder DisableAliases() { preProcessingPhaseObjectGraphVisitorFactories.Remove(typeof(AnchorAssigner)); emissionPhaseObjectGraphVisitorFactories.Remove(typeof(AnchorAssigningObjectGraphVisitor)); return this; } [Obsolete("The default behavior is now to always emit default values, thefore calling this method has no effect. This behavior is now controlled by ConfigureDefaultValuesHandling.", true)] public StaticSerializerBuilder EmitDefaults() { return ConfigureDefaultValuesHandling(DefaultValuesHandling.Preserve); } public StaticSerializerBuilder ConfigureDefaultValuesHandling(DefaultValuesHandling configuration) { defaultValuesHandlingConfiguration = configuration; return this; } public StaticSerializerBuilder JsonCompatible() { emitterSettings = emitterSettings.WithMaxSimpleKeyLength(int.MaxValue).WithoutAnchorName().WithUtf16SurrogatePairs(); return WithTypeConverter(new YamlDotNet.Serialization.Converters.GuidConverter(jsonCompatible: true), delegate(IRegistrationLocationSelectionSyntax w) { w.InsteadOf(); }).WithTypeConverter(new DateTime8601Converter(ScalarStyle.DoubleQuoted)).WithEventEmitter((IEventEmitter inner) => new JsonEventEmitter(inner, yamlFormatter, enumNamingConvention, BuildTypeInspector()), delegate(IRegistrationLocationSelectionSyntax loc) { loc.InsteadOf(); }); } public StaticSerializerBuilder WithNewLine(string newLine) { emitterSettings = emitterSettings.WithNewLine(newLine); return this; } public StaticSerializerBuilder WithPreProcessingPhaseObjectGraphVisitor(TObjectGraphVisitor objectGraphVisitor) where TObjectGraphVisitor : IObjectGraphVisitor { return WithPreProcessingPhaseObjectGraphVisitor(objectGraphVisitor, delegate(IRegistrationLocationSelectionSyntax> w) { w.OnTop(); }); } public StaticSerializerBuilder WithPreProcessingPhaseObjectGraphVisitor(Func, TObjectGraphVisitor> objectGraphVisitorFactory) where TObjectGraphVisitor : IObjectGraphVisitor { return WithPreProcessingPhaseObjectGraphVisitor(objectGraphVisitorFactory, delegate(IRegistrationLocationSelectionSyntax> w) { w.OnTop(); }); } public StaticSerializerBuilder WithPreProcessingPhaseObjectGraphVisitor(TObjectGraphVisitor objectGraphVisitor, Action>> where) where TObjectGraphVisitor : IObjectGraphVisitor { if (objectGraphVisitor == null) { throw new ArgumentNullException("objectGraphVisitor"); } if (where == null) { throw new ArgumentNullException("where"); } where(preProcessingPhaseObjectGraphVisitorFactories.CreateRegistrationLocationSelector(typeof(TObjectGraphVisitor), (IEnumerable _) => objectGraphVisitor)); return this; } public StaticSerializerBuilder WithPreProcessingPhaseObjectGraphVisitor(Func, TObjectGraphVisitor> objectGraphVisitorFactory, Action>> where) where TObjectGraphVisitor : IObjectGraphVisitor { if (objectGraphVisitorFactory == null) { throw new ArgumentNullException("objectGraphVisitorFactory"); } if (where == null) { throw new ArgumentNullException("where"); } where(preProcessingPhaseObjectGraphVisitorFactories.CreateRegistrationLocationSelector(typeof(TObjectGraphVisitor), (IEnumerable typeConverters) => objectGraphVisitorFactory(typeConverters))); return this; } public StaticSerializerBuilder WithPreProcessingPhaseObjectGraphVisitor(WrapperFactory, TObjectGraphVisitor> objectGraphVisitorFactory, Action>> where) where TObjectGraphVisitor : IObjectGraphVisitor { if (objectGraphVisitorFactory == null) { throw new ArgumentNullException("objectGraphVisitorFactory"); } if (where == null) { throw new ArgumentNullException("where"); } where(preProcessingPhaseObjectGraphVisitorFactories.CreateTrackingRegistrationLocationSelector(typeof(TObjectGraphVisitor), (IObjectGraphVisitor wrapped, IEnumerable _) => objectGraphVisitorFactory(wrapped))); return this; } public StaticSerializerBuilder WithPreProcessingPhaseObjectGraphVisitor(WrapperFactory, IObjectGraphVisitor, TObjectGraphVisitor> objectGraphVisitorFactory, Action>> where) where TObjectGraphVisitor : IObjectGraphVisitor { if (objectGraphVisitorFactory == null) { throw new ArgumentNullException("objectGraphVisitorFactory"); } if (where == null) { throw new ArgumentNullException("where"); } where(preProcessingPhaseObjectGraphVisitorFactories.CreateTrackingRegistrationLocationSelector(typeof(TObjectGraphVisitor), (IObjectGraphVisitor wrapped, IEnumerable typeConverters) => objectGraphVisitorFactory(wrapped, typeConverters))); return this; } public StaticSerializerBuilder WithoutPreProcessingPhaseObjectGraphVisitor() where TObjectGraphVisitor : IObjectGraphVisitor { return WithoutPreProcessingPhaseObjectGraphVisitor(typeof(TObjectGraphVisitor)); } public StaticSerializerBuilder WithoutPreProcessingPhaseObjectGraphVisitor(Type objectGraphVisitorType) { if (objectGraphVisitorType == null) { throw new ArgumentNullException("objectGraphVisitorType"); } preProcessingPhaseObjectGraphVisitorFactories.Remove(objectGraphVisitorType); return this; } public StaticSerializerBuilder WithObjectGraphTraversalStrategyFactory(ObjectGraphTraversalStrategyFactory objectGraphTraversalStrategyFactory) { this.objectGraphTraversalStrategyFactory = objectGraphTraversalStrategyFactory; return this; } public StaticSerializerBuilder WithEmissionPhaseObjectGraphVisitor(Func objectGraphVisitorFactory) where TObjectGraphVisitor : IObjectGraphVisitor { return WithEmissionPhaseObjectGraphVisitor(objectGraphVisitorFactory, delegate(IRegistrationLocationSelectionSyntax> w) { w.OnTop(); }); } public StaticSerializerBuilder WithEmissionPhaseObjectGraphVisitor(Func objectGraphVisitorFactory, Action>> where) where TObjectGraphVisitor : IObjectGraphVisitor { if (objectGraphVisitorFactory == null) { throw new ArgumentNullException("objectGraphVisitorFactory"); } if (where == null) { throw new ArgumentNullException("where"); } where(emissionPhaseObjectGraphVisitorFactories.CreateRegistrationLocationSelector(typeof(TObjectGraphVisitor), (EmissionPhaseObjectGraphVisitorArgs args) => objectGraphVisitorFactory(args))); return this; } public StaticSerializerBuilder WithEmissionPhaseObjectGraphVisitor(WrapperFactory, TObjectGraphVisitor> objectGraphVisitorFactory, Action>> where) where TObjectGraphVisitor : IObjectGraphVisitor { if (objectGraphVisitorFactory == null) { throw new ArgumentNullException("objectGraphVisitorFactory"); } if (where == null) { throw new ArgumentNullException("where"); } where(emissionPhaseObjectGraphVisitorFactories.CreateTrackingRegistrationLocationSelector(typeof(TObjectGraphVisitor), (IObjectGraphVisitor wrapped, EmissionPhaseObjectGraphVisitorArgs args) => objectGraphVisitorFactory(wrapped, args))); return this; } public StaticSerializerBuilder WithoutEmissionPhaseObjectGraphVisitor() where TObjectGraphVisitor : IObjectGraphVisitor { return WithoutEmissionPhaseObjectGraphVisitor(typeof(TObjectGraphVisitor)); } public StaticSerializerBuilder WithoutEmissionPhaseObjectGraphVisitor(Type objectGraphVisitorType) { if (objectGraphVisitorType == null) { throw new ArgumentNullException("objectGraphVisitorType"); } emissionPhaseObjectGraphVisitorFactories.Remove(objectGraphVisitorType); return this; } public StaticSerializerBuilder WithIndentedSequences() { emitterSettings = emitterSettings.WithIndentedSequences(); return this; } public ISerializer Build() { return Serializer.FromValueSerializer(BuildValueSerializer(), emitterSettings); } public IValueSerializer BuildValueSerializer() { IEnumerable typeConverters = BuildTypeConverters(); ITypeInspector typeInspector = BuildTypeInspector(); IObjectGraphTraversalStrategy traversalStrategy = objectGraphTraversalStrategyFactory(typeInspector, typeResolver, typeConverters, maximumRecursion); IEventEmitter eventEmitter = eventEmitterFactories.BuildComponentChain(new WriterEventEmitter()); return new ValueSerializer(traversalStrategy, eventEmitter, typeConverters, preProcessingPhaseObjectGraphVisitorFactories.Clone(), emissionPhaseObjectGraphVisitorFactories.Clone()); } public ITypeInspector BuildTypeInspector() { ITypeInspector typeInspector = context.GetTypeInspector(); return typeInspectorFactories.BuildComponentChain(typeInspector); } } internal sealed class StreamFragment : IYamlConvertible { private readonly List events = new List(); public IList Events => events; void IYamlConvertible.Read(IParser parser, Type expectedType, ObjectDeserializer nestedObjectDeserializer) { events.Clear(); int num = 0; do { if (!parser.MoveNext()) { throw new InvalidOperationException("The parser has reached the end before deserialization completed."); } ParsingEvent current = parser.Current; events.Add(current); num += current.NestingIncrease; } while (num > 0); } void IYamlConvertible.Write(IEmitter emitter, ObjectSerializer nestedObjectSerializer) { foreach (ParsingEvent @event in events) { emitter.Emit(@event); } } } internal sealed class TagMappings { private readonly Dictionary mappings; public TagMappings() { mappings = new Dictionary(); } public TagMappings(IDictionary mappings) { this.mappings = new Dictionary(mappings); } public void Add(string tag, Type mapping) { mappings.Add(tag, mapping); } internal Type? GetMapping(string tag) { if (!mappings.TryGetValue(tag, out Type value)) { return null; } return value; } } internal sealed class YamlAttributeOverrides { private readonly struct AttributeKey { public readonly Type AttributeType; public readonly string PropertyName; public AttributeKey(Type attributeType, string propertyName) { AttributeType = attributeType; PropertyName = propertyName; } public override bool Equals(object? obj) { if (obj is AttributeKey attributeKey && AttributeType.Equals(attributeKey.AttributeType)) { return PropertyName.Equals(attributeKey.PropertyName); } return false; } public override int GetHashCode() { return YamlDotNet.Core.HashCode.CombineHashCodes(AttributeType.GetHashCode(), PropertyName.GetHashCode()); } } private sealed class AttributeMapping { public readonly Type RegisteredType; public readonly Attribute Attribute; public AttributeMapping(Type registeredType, Attribute attribute) { RegisteredType = registeredType; Attribute = attribute; } public override bool Equals(object? obj) { if (obj is AttributeMapping attributeMapping && RegisteredType.Equals(attributeMapping.RegisteredType)) { return Attribute.Equals(attributeMapping.Attribute); } return false; } public override int GetHashCode() { return YamlDotNet.Core.HashCode.CombineHashCodes(RegisteredType.GetHashCode(), Attribute.GetHashCode()); } public int Matches(Type matchType) { int num = 0; Type type = matchType; while (type != null) { num++; if (type == RegisteredType) { return num; } type = type.BaseType(); } if (matchType.GetInterfaces().Contains(RegisteredType)) { return num; } return 0; } } private readonly Dictionary> overrides = new Dictionary>(); [return: MaybeNull] public T GetAttribute(Type type, string member) where T : Attribute { if (overrides.TryGetValue(new AttributeKey(typeof(T), member), out List value)) { int num = 0; AttributeMapping attributeMapping = null; foreach (AttributeMapping item in value) { int num2 = item.Matches(type); if (num2 > num) { num = num2; attributeMapping = item; } } if (num > 0) { return (T)attributeMapping.Attribute; } } return null; } public void Add(Type type, string member, Attribute attribute) { AttributeMapping item = new AttributeMapping(type, attribute); AttributeKey key = new AttributeKey(attribute.GetType(), member); if (!overrides.TryGetValue(key, out List value)) { value = new List(); overrides.Add(key, value); } else if (value.Contains(item)) { throw new InvalidOperationException($"Attribute ({attribute}) already set for Type {type.FullName}, Member {member}"); } value.Add(item); } public YamlAttributeOverrides Clone() { YamlAttributeOverrides yamlAttributeOverrides = new YamlAttributeOverrides(); foreach (KeyValuePair> @override in overrides) { foreach (AttributeMapping item in @override.Value) { yamlAttributeOverrides.Add(item.RegisteredType, @override.Key.PropertyName, item.Attribute); } } return yamlAttributeOverrides; } public void Add(Expression> propertyAccessor, Attribute attribute) { PropertyInfo propertyInfo = propertyAccessor.AsProperty(); Add(typeof(TClass), propertyInfo.Name, attribute); } } internal sealed class YamlAttributeOverridesInspector : ReflectionTypeInspector { public sealed class OverridePropertyDescriptor : IPropertyDescriptor { private readonly IPropertyDescriptor baseDescriptor; private readonly YamlAttributeOverrides overrides; private readonly Type classType; public string Name => baseDescriptor.Name; public bool Required => baseDescriptor.Required; public bool AllowNulls => baseDescriptor.AllowNulls; public bool CanWrite => baseDescriptor.CanWrite; public Type Type => baseDescriptor.Type; public Type? TypeOverride { get { return baseDescriptor.TypeOverride; } set { baseDescriptor.TypeOverride = value; } } public Type? ConverterType => GetCustomAttribute()?.ConverterType ?? baseDescriptor.ConverterType; public int Order { get { return baseDescriptor.Order; } set { baseDescriptor.Order = value; } } public ScalarStyle ScalarStyle { get { return baseDescriptor.ScalarStyle; } set { baseDescriptor.ScalarStyle = value; } } public OverridePropertyDescriptor(IPropertyDescriptor baseDescriptor, YamlAttributeOverrides overrides, Type classType) { this.baseDescriptor = baseDescriptor; this.overrides = overrides; this.classType = classType; } public void Write(object target, object? value) { baseDescriptor.Write(target, value); } public T? GetCustomAttribute() where T : Attribute { T attribute = overrides.GetAttribute(classType, Name); return attribute ?? baseDescriptor.GetCustomAttribute(); } public IObjectDescriptor Read(object target) { return baseDescriptor.Read(target); } } private readonly ITypeInspector innerTypeDescriptor; private readonly YamlAttributeOverrides overrides; public YamlAttributeOverridesInspector(ITypeInspector innerTypeDescriptor, YamlAttributeOverrides overrides) { this.innerTypeDescriptor = innerTypeDescriptor; this.overrides = overrides; } public override IEnumerable GetProperties(Type type, object? container) { IEnumerable enumerable = innerTypeDescriptor.GetProperties(type, container); if (overrides != null) { enumerable = enumerable.Select((Func)((IPropertyDescriptor p) => new OverridePropertyDescriptor(p, overrides, type))); } return enumerable; } } internal sealed class YamlAttributesTypeInspector : TypeInspectorSkeleton { private readonly ITypeInspector innerTypeDescriptor; public YamlAttributesTypeInspector(ITypeInspector innerTypeDescriptor) { this.innerTypeDescriptor = innerTypeDescriptor; } public override string GetEnumName(Type enumType, string name) { return innerTypeDescriptor.GetEnumName(enumType, name); } public override string GetEnumValue(object enumValue) { return innerTypeDescriptor.GetEnumValue(enumValue); } public override IEnumerable GetProperties(Type type, object? container) { return from p in (from p in innerTypeDescriptor.GetProperties(type, container) where p.GetCustomAttribute() == null select p).Select((Func)delegate(IPropertyDescriptor p) { PropertyDescriptor propertyDescriptor = new PropertyDescriptor(p); YamlMemberAttribute customAttribute = p.GetCustomAttribute(); if (customAttribute != null) { if (customAttribute.SerializeAs != null) { propertyDescriptor.TypeOverride = customAttribute.SerializeAs; } propertyDescriptor.Order = customAttribute.Order; propertyDescriptor.ScalarStyle = customAttribute.ScalarStyle; if (customAttribute.Alias != null) { propertyDescriptor.Name = customAttribute.Alias; } } return propertyDescriptor; }) orderby p.Order select p; } } [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)] internal sealed class YamlConverterAttribute : Attribute { public Type ConverterType { get; } public YamlConverterAttribute(Type converterType) { ConverterType = converterType; } } internal class YamlFormatter { public static YamlFormatter Default { get; } = new YamlFormatter(); public NumberFormatInfo NumberFormat { get; set; } = new NumberFormatInfo { CurrencyDecimalSeparator = ".", CurrencyGroupSeparator = "_", CurrencyGroupSizes = new int[1] { 3 }, CurrencySymbol = string.Empty, CurrencyDecimalDigits = 99, NumberDecimalSeparator = ".", NumberGroupSeparator = "_", NumberGroupSizes = new int[1] { 3 }, NumberDecimalDigits = 99, NaNSymbol = ".nan", PositiveInfinitySymbol = ".inf", NegativeInfinitySymbol = "-.inf" }; public virtual Func FormatEnum { get; set; } = delegate(object value, ITypeInspector typeInspector, INamingConvention enumNamingConvention) { string empty = string.Empty; empty = ((value != null) ? typeInspector.GetEnumValue(value) : string.Empty); return enumNamingConvention.Apply(empty); }; public virtual Func PotentiallyQuoteEnums { get; set; } = (object _) => true; public string FormatNumber(object number) { return Convert.ToString(number, NumberFormat); } public string FormatNumber(double number) { return number.ToString("G", NumberFormat); } public string FormatNumber(float number) { return number.ToString("G", NumberFormat); } public string FormatBoolean(object boolean) { if (!boolean.Equals(true)) { return "false"; } return "true"; } public string FormatDateTime(object dateTime) { return ((DateTime)dateTime).ToString("o", CultureInfo.InvariantCulture); } public string FormatTimeSpan(object timeSpan) { return ((TimeSpan)timeSpan/*cast due to .constrained prefix*/).ToString(); } } [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false)] internal sealed class YamlIgnoreAttribute : Attribute { } [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false)] internal sealed class YamlMemberAttribute : Attribute { private DefaultValuesHandling? defaultValuesHandling; public string? Description { get; set; } public Type? SerializeAs { get; set; } public int Order { get; set; } public string? Alias { get; set; } public bool ApplyNamingConventions { get; set; } public ScalarStyle ScalarStyle { get; set; } public DefaultValuesHandling DefaultValuesHandling { get { return defaultValuesHandling.GetValueOrDefault(); } set { defaultValuesHandling = value; } } public bool IsDefaultValuesHandlingSpecified => defaultValuesHandling.HasValue; public YamlMemberAttribute() { ScalarStyle = ScalarStyle.Any; ApplyNamingConventions = true; } public YamlMemberAttribute(Type serializeAs) : this() { SerializeAs = serializeAs ?? throw new ArgumentNullException("serializeAs"); } } [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum, Inherited = false, AllowMultiple = true)] internal sealed class YamlSerializableAttribute : Attribute { public YamlSerializableAttribute() { } public YamlSerializableAttribute(Type serializableType) { } } [AttributeUsage(AttributeTargets.Class)] internal sealed class YamlStaticContextAttribute : Attribute { } } namespace YamlDotNet.Serialization.ValueDeserializers { internal sealed class AliasValueDeserializer : IValueDeserializer { private sealed class AliasState : Dictionary, IPostDeserializationCallback { public void OnDeserialization() { foreach (ValuePromise value in base.Values) { if (!value.HasValue) { YamlDotNet.Core.Events.AnchorAlias alias = value.Alias; throw new AnchorNotFoundException(alias.Start, alias.End, $"Anchor '{alias.Value}' not found"); } } } } private sealed class ValuePromise : IValuePromise { private object? value; public readonly YamlDotNet.Core.Events.AnchorAlias? Alias; public bool HasValue { get; private set; } public object? Value { get { if (!HasValue) { throw new InvalidOperationException("Value not set"); } return value; } set { if (HasValue) { throw new InvalidOperationException("Value already set"); } HasValue = true; this.value = value; this.ValueAvailable?.Invoke(value); } } public event Action? ValueAvailable; public ValuePromise(YamlDotNet.Core.Events.AnchorAlias alias) { Alias = alias; } public ValuePromise(object? value) { HasValue = true; this.value = value; } } private readonly IValueDeserializer innerDeserializer; public AliasValueDeserializer(IValueDeserializer innerDeserializer) { this.innerDeserializer = innerDeserializer ?? throw new ArgumentNullException("innerDeserializer"); } public object? DeserializeValue(IParser parser, Type expectedType, SerializerState state, IValueDeserializer nestedObjectDeserializer) { if (parser.TryConsume(out var @event)) { AliasState aliasState = state.Get(); if (!aliasState.TryGetValue(@event.Value, out ValuePromise value)) { throw new AnchorNotFoundException(@event.Start, @event.End, $"Alias ${@event.Value} cannot precede anchor declaration"); } if (!value.HasValue) { return value; } return value.Value; } AnchorName anchorName = AnchorName.Empty; if (parser.Accept(out var event2) && !event2.Anchor.IsEmpty) { anchorName = event2.Anchor; AliasState aliasState2 = state.Get(); if (!aliasState2.ContainsKey(anchorName)) { aliasState2[anchorName] = new ValuePromise(new YamlDotNet.Core.Events.AnchorAlias(anchorName)); } } object obj = innerDeserializer.DeserializeValue(parser, expectedType, state, nestedObjectDeserializer); if (!anchorName.IsEmpty) { AliasState aliasState3 = state.Get(); if (!aliasState3.TryGetValue(anchorName, out ValuePromise value2)) { aliasState3.Add(anchorName, new ValuePromise(obj)); } else if (!value2.HasValue) { value2.Value = obj; } else { aliasState3[anchorName] = new ValuePromise(obj); } } return obj; } } internal sealed class NodeValueDeserializer : IValueDeserializer { private readonly IList deserializers; private readonly IList typeResolvers; private readonly ITypeConverter typeConverter; private readonly INamingConvention enumNamingConvention; private readonly ITypeInspector typeInspector; public NodeValueDeserializer(IList deserializers, IList typeResolvers, ITypeConverter typeConverter, INamingConvention enumNamingConvention, ITypeInspector typeInspector) { this.deserializers = deserializers ?? throw new ArgumentNullException("deserializers"); this.typeResolvers = typeResolvers ?? throw new ArgumentNullException("typeResolvers"); this.typeConverter = typeConverter ?? throw new ArgumentNullException("typeConverter"); this.enumNamingConvention = enumNamingConvention ?? throw new ArgumentNullException("enumNamingConvention"); this.typeInspector = typeInspector; } public object? DeserializeValue(IParser parser, Type expectedType, SerializerState state, IValueDeserializer nestedObjectDeserializer) { parser.Accept(out var @event); Type typeFromEvent = GetTypeFromEvent(@event, expectedType); ObjectDeserializer rootDeserializer = (Type x) => DeserializeValue(parser, x, state, nestedObjectDeserializer); try { foreach (INodeDeserializer deserializer in deserializers) { if (deserializer.Deserialize(parser, typeFromEvent, (IParser r, Type t) => nestedObjectDeserializer.DeserializeValue(r, t, state, nestedObjectDeserializer), out object value, rootDeserializer)) { return typeConverter.ChangeType(value, expectedType, enumNamingConvention, typeInspector); } } } catch (YamlException) { throw; } catch (Exception innerException) { throw new YamlException(@event?.Start ?? Mark.Empty, @event?.End ?? Mark.Empty, "Exception during deserialization", innerException); } throw new YamlException(@event?.Start ?? Mark.Empty, @event?.End ?? Mark.Empty, "No node deserializer was able to deserialize the node into type " + expectedType.AssemblyQualifiedName); } private Type GetTypeFromEvent(NodeEvent? nodeEvent, Type currentType) { foreach (INodeTypeResolver typeResolver in typeResolvers) { if (typeResolver.Resolve(nodeEvent, ref currentType)) { break; } } return currentType; } } } namespace YamlDotNet.Serialization.Utilities { internal interface IPostDeserializationCallback { void OnDeserialization(); } internal interface ITypeConverter { object? ChangeType(object? value, Type expectedType, INamingConvention enumNamingConvention, ITypeInspector typeInspector); } internal class NullTypeConverter : ITypeConverter { public object? ChangeType(object? value, Type expectedType, INamingConvention enumNamingConvention, ITypeInspector typeInspector) { return value; } } internal sealed class ObjectAnchorCollection { private readonly Dictionary objectsByAnchor = new Dictionary(); private readonly Dictionary anchorsByObject = new Dictionary(); public object this[string anchor] { get { if (objectsByAnchor.TryGetValue(anchor, out object value)) { return value; } throw new AnchorNotFoundException("The anchor '" + anchor + "' does not exists"); } } public void Add(string anchor, object @object) { objectsByAnchor.Add(anchor, @object); if (@object != null) { anchorsByObject.Add(@object, anchor); } } public bool TryGetAnchor(object @object, [MaybeNullWhen(false)] out string? anchor) { return anchorsByObject.TryGetValue(@object, out anchor); } } internal class ReflectionTypeConverter : ITypeConverter { public object? ChangeType(object? value, Type expectedType, ITypeInspector typeInspector) { return ChangeType(value, expectedType, NullNamingConvention.Instance, typeInspector); } public object? ChangeType(object? value, Type expectedType, INamingConvention enumNamingConvention, ITypeInspector typeInspector) { return TypeConverter.ChangeType(value, expectedType, enumNamingConvention, typeInspector); } } internal sealed class SerializerState : IDisposable { private readonly Dictionary items = new Dictionary(); public T Get() where T : class, new() { if (!items.TryGetValue(typeof(T), out object value)) { value = new T(); items.Add(typeof(T), value); } return (T)value; } public void OnDeserialization() { foreach (IPostDeserializationCallback item in items.Values.OfType()) { item.OnDeserialization(); } } public void Dispose() { foreach (IDisposable item in items.Values.OfType()) { item.Dispose(); } } } internal static class StringExtensions { private static string ToCamelOrPascalCase(string str, Func firstLetterTransform) { string text = Regex.Replace(str, "([_\\-])(?[a-z])", (Match match) => match.Groups["char"].Value.ToUpperInvariant(), RegexOptions.IgnoreCase); return firstLetterTransform(text[0]) + text.Substring(1); } public static string ToCamelCase(this string str) { return ToCamelOrPascalCase(str, char.ToLowerInvariant); } public static string ToPascalCase(this string str) { return ToCamelOrPascalCase(str, char.ToUpperInvariant); } public static string FromCamelCase(this string str, string separator) { str = char.ToLower(str[0], CultureInfo.InvariantCulture) + str.Substring(1); str = Regex.Replace(str.ToCamelCase(), "(?[A-Z])", (Match match) => separator + match.Groups["char"].Value.ToLowerInvariant()); return str; } } internal static class TypeConverter { public static T ChangeType(object? value, INamingConvention enumNamingConvention, ITypeInspector typeInspector) { return (T)ChangeType(value, typeof(T), enumNamingConvention, typeInspector); } public static object? ChangeType(object? value, Type destinationType, INamingConvention enumNamingConvention, ITypeInspector typeInspector) { return ChangeType(value, destinationType, CultureInfo.InvariantCulture, enumNamingConvention, typeInspector); } public static object? ChangeType(object? value, Type destinationType, IFormatProvider provider, INamingConvention enumNamingConvention, ITypeInspector typeInspector) { return ChangeType(value, destinationType, new CultureInfoAdapter(CultureInfo.CurrentCulture, provider), enumNamingConvention, typeInspector); } public static object? ChangeType(object? value, Type destinationType, CultureInfo culture, INamingConvention enumNamingConvention, ITypeInspector typeInspector) { if (value == null || value.IsDbNull()) { if (!destinationType.IsValueType()) { return null; } return Activator.CreateInstance(destinationType); } Type type = value.GetType(); if (destinationType == type || destinationType.IsAssignableFrom(type)) { return value; } if (destinationType.IsGenericType()) { Type genericTypeDefinition = destinationType.GetGenericTypeDefinition(); if (genericTypeDefinition == typeof(Nullable<>) || FsharpHelper.IsOptionType(genericTypeDefinition)) { Type destinationType2 = destinationType.GetGenericArguments()[0]; object obj = ChangeType(value, destinationType2, culture, enumNamingConvention, typeInspector); return Activator.CreateInstance(destinationType, obj); } } if (destinationType.IsEnum()) { object result = value; if (value is string value2) { string name = enumNamingConvention.Reverse(value2); name = typeInspector.GetEnumName(destinationType, name); result = Enum.Parse(destinationType, name, ignoreCase: true); } return result; } if (destinationType == typeof(bool)) { if ("0".Equals(value)) { return false; } if ("1".Equals(value)) { return true; } } System.ComponentModel.TypeConverter converter = TypeDescriptor.GetConverter(type); if (converter != null && converter.CanConvertTo(destinationType)) { return converter.ConvertTo(null, culture, value, destinationType); } System.ComponentModel.TypeConverter converter2 = TypeDescriptor.GetConverter(destinationType); if (converter2 != null && converter2.CanConvertFrom(type)) { return converter2.ConvertFrom(null, culture, value); } Type[] array = new Type[2] { type, destinationType }; foreach (Type type2 in array) { foreach (MethodInfo publicStaticMethod2 in type2.GetPublicStaticMethods()) { if (!publicStaticMethod2.IsSpecialName || (!(publicStaticMethod2.Name == "op_Implicit") && !(publicStaticMethod2.Name == "op_Explicit")) || !destinationType.IsAssignableFrom(publicStaticMethod2.ReturnParameter.ParameterType)) { continue; } ParameterInfo[] parameters = publicStaticMethod2.GetParameters(); if (parameters.Length == 1 && parameters[0].ParameterType.IsAssignableFrom(type)) { try { return publicStaticMethod2.Invoke(null, new object[1] { value }); } catch (TargetInvocationException ex) { throw ex.InnerException; } } } } if (type == typeof(string)) { try { MethodInfo publicStaticMethod = destinationType.GetPublicStaticMethod("Parse", typeof(string), typeof(IFormatProvider)); if (publicStaticMethod != null) { return publicStaticMethod.Invoke(null, new object[2] { value, culture }); } publicStaticMethod = destinationType.GetPublicStaticMethod("Parse", typeof(string)); if (publicStaticMethod != null) { return publicStaticMethod.Invoke(null, new object[1] { value }); } } catch (TargetInvocationException ex2) { throw ex2.InnerException; } } if (destinationType == typeof(TimeSpan)) { return TimeSpan.Parse((string)ChangeType(value, typeof(string), CultureInfo.InvariantCulture, enumNamingConvention, typeInspector), CultureInfo.InvariantCulture); } return Convert.ChangeType(value, destinationType, CultureInfo.InvariantCulture); } public static void RegisterTypeConverter() where TConverter : System.ComponentModel.TypeConverter { if (!TypeDescriptor.GetAttributes(typeof(TConvertible)).OfType().Any((TypeConverterAttribute a) => a.ConverterTypeName == typeof(TConverter).AssemblyQualifiedName)) { TypeDescriptor.AddAttributes(typeof(TConvertible), new TypeConverterAttribute(typeof(TConverter))); } } } internal sealed class TypeConverterCache { private readonly IYamlTypeConverter[] typeConverters; private readonly ConcurrentDictionary cache = new ConcurrentDictionary(); public TypeConverterCache(IEnumerable? typeConverters) : this(typeConverters?.ToArray() ?? Array.Empty()) { } public TypeConverterCache(IYamlTypeConverter[] typeConverters) { this.typeConverters = typeConverters; } public bool TryGetConverterForType(Type type, [NotNullWhen(true)] out IYamlTypeConverter? typeConverter) { (bool, IYamlTypeConverter) orAdd = DictionaryExtensions.GetOrAdd(cache, type, (Type t, IYamlTypeConverter[] tc) => LookupTypeConverter(t, tc), typeConverters); typeConverter = orAdd.Item2; return orAdd.Item1; } public IYamlTypeConverter GetConverterByType(Type converter) { IYamlTypeConverter[] array = typeConverters; foreach (IYamlTypeConverter yamlTypeConverter in array) { if (yamlTypeConverter.GetType() == converter) { return yamlTypeConverter; } } throw new ArgumentException("IYamlTypeConverter of type " + converter.FullName + " not found", "converter"); } private static (bool HasMatch, IYamlTypeConverter? TypeConverter) LookupTypeConverter(Type type, IYamlTypeConverter[] typeConverters) { foreach (IYamlTypeConverter yamlTypeConverter in typeConverters) { if (yamlTypeConverter.Accepts(type)) { return (HasMatch: true, TypeConverter: yamlTypeConverter); } } return (HasMatch: false, TypeConverter: null); } } } namespace YamlDotNet.Serialization.TypeResolvers { internal sealed class DynamicTypeResolver : ITypeResolver { public Type Resolve(Type staticType, object? actualValue) { if (actualValue == null) { return staticType; } return actualValue.GetType(); } } internal class StaticTypeResolver : ITypeResolver { public virtual Type Resolve(Type staticType, object? actualValue) { if (actualValue != null) { if (actualValue.GetType().IsEnum) { return staticType; } switch (actualValue.GetType().GetTypeCode()) { case TypeCode.Boolean: return typeof(bool); case TypeCode.Char: return typeof(char); case TypeCode.SByte: return typeof(sbyte); case TypeCode.Byte: return typeof(byte); case TypeCode.Int16: return typeof(short); case TypeCode.UInt16: return typeof(ushort); case TypeCode.Int32: return typeof(int); case TypeCode.UInt32: return typeof(uint); case TypeCode.Int64: return typeof(long); case TypeCode.UInt64: return typeof(ulong); case TypeCode.Single: return typeof(float); case TypeCode.Double: return typeof(double); case TypeCode.Decimal: return typeof(decimal); case TypeCode.String: return typeof(string); case TypeCode.DateTime: return typeof(DateTime); } } return staticType; } } } namespace YamlDotNet.Serialization.TypeInspectors { internal class CachedTypeInspector : TypeInspectorSkeleton { private readonly ITypeInspector innerTypeDescriptor; private readonly ConcurrentDictionary> cache = new ConcurrentDictionary>(); private readonly ConcurrentDictionary> enumNameCache = new ConcurrentDictionary>(); private readonly ConcurrentDictionary enumValueCache = new ConcurrentDictionary(); public CachedTypeInspector(ITypeInspector innerTypeDescriptor) { this.innerTypeDescriptor = innerTypeDescriptor ?? throw new ArgumentNullException("innerTypeDescriptor"); } public override string GetEnumName(Type enumType, string name) { ConcurrentDictionary orAdd = enumNameCache.GetOrAdd(enumType, (Type _) => new ConcurrentDictionary()); return DictionaryExtensions.GetOrAdd(orAdd, name, delegate(string n, (Type enumType, ITypeInspector innerTypeDescriptor) context) { var (enumType2, typeInspector) = context; return typeInspector.GetEnumName(enumType2, n); }, (enumType, innerTypeDescriptor)); } public override string GetEnumValue(object enumValue) { return DictionaryExtensions.GetOrAdd(enumValueCache, enumValue, delegate(object _, (object enumValue, ITypeInspector innerTypeDescriptor) context) { var (enumValue2, typeInspector) = context; return typeInspector.GetEnumValue(enumValue2); }, (enumValue, innerTypeDescriptor)); } public override IEnumerable GetProperties(Type type, object? container) { return DictionaryExtensions.GetOrAdd(cache, type, delegate(Type t, (object container, ITypeInspector innerTypeDescriptor) context) { var (container2, typeInspector) = context; return typeInspector.GetProperties(t, container2).ToList(); }, (container, innerTypeDescriptor)); } } internal class CompositeTypeInspector : TypeInspectorSkeleton { private readonly IEnumerable typeInspectors; public CompositeTypeInspector(params ITypeInspector[] typeInspectors) : this((IEnumerable)typeInspectors) { } public CompositeTypeInspector(IEnumerable typeInspectors) { this.typeInspectors = typeInspectors?.ToList() ?? throw new ArgumentNullException("typeInspectors"); } public override string GetEnumName(Type enumType, string name) { foreach (ITypeInspector typeInspector in typeInspectors) { try { return typeInspector.GetEnumName(enumType, name); } catch { } } throw new ArgumentOutOfRangeException("enumType,name", "Name not found on enum type"); } public override string GetEnumValue(object enumValue) { if (enumValue == null) { throw new ArgumentNullException("enumValue"); } foreach (ITypeInspector typeInspector in typeInspectors) { try { return typeInspector.GetEnumValue(enumValue); } catch { } } throw new ArgumentOutOfRangeException("enumValue", $"Value not found for ({enumValue})"); } public override IEnumerable GetProperties(Type type, object? container) { return typeInspectors.SelectMany((ITypeInspector i) => i.GetProperties(type, container)); } } internal class NamingConventionTypeInspector : TypeInspectorSkeleton { private readonly ITypeInspector innerTypeDescriptor; private readonly INamingConvention namingConvention; public NamingConventionTypeInspector(ITypeInspector innerTypeDescriptor, INamingConvention namingConvention) { this.innerTypeDescriptor = innerTypeDescriptor ?? throw new ArgumentNullException("innerTypeDescriptor"); this.namingConvention = namingConvention ?? throw new ArgumentNullException("namingConvention"); } public override string GetEnumName(Type enumType, string name) { return innerTypeDescriptor.GetEnumName(enumType, name); } public override string GetEnumValue(object enumValue) { return innerTypeDescriptor.GetEnumValue(enumValue); } public override IEnumerable GetProperties(Type type, object? container) { return innerTypeDescriptor.GetProperties(type, container).Select(delegate(IPropertyDescriptor p) { YamlMemberAttribute customAttribute = p.GetCustomAttribute(); return (customAttribute != null && !customAttribute.ApplyNamingConventions) ? p : new PropertyDescriptor(p) { Name = namingConvention.Apply(p.Name) }; }); } } internal class ReadableAndWritablePropertiesTypeInspector : TypeInspectorSkeleton { private readonly ITypeInspector innerTypeDescriptor; public ReadableAndWritablePropertiesTypeInspector(ITypeInspector innerTypeDescriptor) { this.innerTypeDescriptor = innerTypeDescriptor ?? throw new ArgumentNullException("innerTypeDescriptor"); } public override string GetEnumName(Type enumType, string name) { return innerTypeDescriptor.GetEnumName(enumType, name); } public override string GetEnumValue(object enumValue) { return innerTypeDescriptor.GetEnumValue(enumValue); } public override IEnumerable GetProperties(Type type, object? container) { return from p in innerTypeDescriptor.GetProperties(type, container) where p.CanWrite select p; } } internal class ReadableFieldsTypeInspector : ReflectionTypeInspector { protected class ReflectionFieldDescriptor : IPropertyDescriptor { private readonly FieldInfo fieldInfo; private readonly ITypeResolver typeResolver; public string Name => fieldInfo.Name; public bool Required => fieldInfo.IsRequired(); public Type Type => fieldInfo.FieldType; public Type? ConverterType { get; } public Type? TypeOverride { get; set; } public bool AllowNulls => fieldInfo.AcceptsNull(); public int Order { get; set; } public bool CanWrite => !fieldInfo.IsInitOnly; public ScalarStyle ScalarStyle { get; set; } public ReflectionFieldDescriptor(FieldInfo fieldInfo, ITypeResolver typeResolver) { this.fieldInfo = fieldInfo; this.typeResolver = typeResolver; YamlConverterAttribute customAttribute = fieldInfo.GetCustomAttribute(); if (customAttribute != null) { ConverterType = customAttribute.ConverterType; } ScalarStyle = ScalarStyle.Any; } public void Write(object target, object? value) { fieldInfo.SetValue(target, value); } public T? GetCustomAttribute() where T : Attribute { object[] customAttributes = fieldInfo.GetCustomAttributes(typeof(T), inherit: true); return (T)customAttributes.FirstOrDefault(); } public IObjectDescriptor Read(object target) { object value = fieldInfo.GetValue(target); Type type = TypeOverride ?? typeResolver.Resolve(Type, value); return new ObjectDescriptor(value, type, Type, ScalarStyle); } } private readonly ITypeResolver typeResolver; public ReadableFieldsTypeInspector(ITypeResolver typeResolver) { this.typeResolver = typeResolver ?? throw new ArgumentNullException("typeResolver"); } public override IEnumerable GetProperties(Type type, object? container) { return type.GetPublicFields().Select((Func)((FieldInfo p) => new ReflectionFieldDescriptor(p, typeResolver))); } } internal class ReadablePropertiesTypeInspector : ReflectionTypeInspector { protected class ReflectionPropertyDescriptor : IPropertyDescriptor { private readonly PropertyInfo propertyInfo; private readonly ITypeResolver typeResolver; public string Name => propertyInfo.Name; public bool Required => propertyInfo.IsRequired(); public Type Type => propertyInfo.PropertyType; public Type? TypeOverride { get; set; } public Type? ConverterType { get; set; } public bool AllowNulls => propertyInfo.AcceptsNull(); public int Order { get; set; } public bool CanWrite => propertyInfo.CanWrite; public ScalarStyle ScalarStyle { get; set; } public ReflectionPropertyDescriptor(PropertyInfo propertyInfo, ITypeResolver typeResolver) { this.propertyInfo = propertyInfo ?? throw new ArgumentNullException("propertyInfo"); this.typeResolver = typeResolver ?? throw new ArgumentNullException("typeResolver"); ScalarStyle = ScalarStyle.Any; YamlConverterAttribute customAttribute = propertyInfo.GetCustomAttribute(); if (customAttribute != null) { ConverterType = customAttribute.ConverterType; } } public void Write(object target, object? value) { propertyInfo.SetValue(target, value, null); } public T? GetCustomAttribute() where T : Attribute { Attribute[] allCustomAttributes = propertyInfo.GetAllCustomAttributes(); return (T)allCustomAttributes.FirstOrDefault(); } public IObjectDescriptor Read(object target) { object obj = propertyInfo.ReadValue(target); Type type = TypeOverride ?? typeResolver.Resolve(Type, obj); return new ObjectDescriptor(obj, type, Type, ScalarStyle); } } private readonly ITypeResolver typeResolver; private readonly bool includeNonPublicProperties; public ReadablePropertiesTypeInspector(ITypeResolver typeResolver) : this(typeResolver, includeNonPublicProperties: false) { } public ReadablePropertiesTypeInspector(ITypeResolver typeResolver, bool includeNonPublicProperties) { this.typeResolver = typeResolver ?? throw new ArgumentNullException("typeResolver"); this.includeNonPublicProperties = includeNonPublicProperties; } private static bool IsValidProperty(PropertyInfo property) { if (property.CanRead) { return property.GetGetMethod(nonPublic: true).GetParameters().Length == 0; } return false; } public override IEnumerable GetProperties(Type type, object? container) { return type.GetProperties(includeNonPublicProperties).Where(IsValidProperty).Select((Func)((PropertyInfo p) => new ReflectionPropertyDescriptor(p, typeResolver))); } } internal abstract class ReflectionTypeInspector : TypeInspectorSkeleton { public override string GetEnumName(Type enumType, string name) { return name; } public override string GetEnumValue(object enumValue) { if (enumValue == null) { return string.Empty; } return enumValue.ToString(); } } internal abstract class TypeInspectorSkeleton : ITypeInspector { public abstract string GetEnumName(Type enumType, string name); public abstract string GetEnumValue(object enumValue); public abstract IEnumerable GetProperties(Type type, object? container); public IPropertyDescriptor GetProperty(Type type, object? container, string name, [MaybeNullWhen(true)] bool ignoreUnmatched, bool caseInsensitivePropertyMatching) { IEnumerable enumerable = ((!caseInsensitivePropertyMatching) ? (from p in GetProperties(type, container) where p.Name == name select p) : (from p in GetProperties(type, container) where p.Name.Equals(name, StringComparison.OrdinalIgnoreCase) select p)); using IEnumerator enumerator = enumerable.GetEnumerator(); if (!enumerator.MoveNext()) { if (ignoreUnmatched) { return null; } throw new SerializationException("Property '" + name + "' not found on type '" + type.FullName + "'."); } IPropertyDescriptor current = enumerator.Current; if (enumerator.MoveNext()) { throw new SerializationException("Multiple properties with the name/alias '" + name + "' already exists on type '" + type.FullName + "', maybe you're misusing YamlAlias or maybe you are using the wrong naming convention? The matching properties are: " + string.Join(", ", enumerable.Select((IPropertyDescriptor p) => p.Name).ToArray())); } return current; } } internal class WritablePropertiesTypeInspector : ReflectionTypeInspector { protected class ReflectionPropertyDescriptor : IPropertyDescriptor { private readonly PropertyInfo propertyInfo; private readonly ITypeResolver typeResolver; public string Name => propertyInfo.Name; public bool Required => propertyInfo.IsRequired(); public Type Type => propertyInfo.PropertyType; public Type? TypeOverride { get; set; } public Type? ConverterType { get; set; } public bool AllowNulls => propertyInfo.AcceptsNull(); public int Order { get; set; } public bool CanWrite => propertyInfo.CanWrite; public ScalarStyle ScalarStyle { get; set; } public ReflectionPropertyDescriptor(PropertyInfo propertyInfo, ITypeResolver typeResolver) { this.propertyInfo = propertyInfo ?? throw new ArgumentNullException("propertyInfo"); this.typeResolver = typeResolver ?? throw new ArgumentNullException("typeResolver"); ScalarStyle = ScalarStyle.Any; YamlConverterAttribute customAttribute = propertyInfo.GetCustomAttribute(); if (customAttribute != null) { ConverterType = customAttribute.ConverterType; } } public void Write(object target, object? value) { propertyInfo.SetValue(target, value, null); } public T? GetCustomAttribute() where T : Attribute { Attribute[] allCustomAttributes = propertyInfo.GetAllCustomAttributes(); return (T)allCustomAttributes.FirstOrDefault(); } public IObjectDescriptor Read(object target) { object obj = propertyInfo.ReadValue(target); Type type = TypeOverride ?? typeResolver.Resolve(Type, obj); return new ObjectDescriptor(obj, type, Type, ScalarStyle); } } private readonly ITypeResolver typeResolver; private readonly bool includeNonPublicProperties; public WritablePropertiesTypeInspector(ITypeResolver typeResolver) : this(typeResolver, includeNonPublicProperties: false) { } public WritablePropertiesTypeInspector(ITypeResolver typeResolver, bool includeNonPublicProperties) { this.typeResolver = typeResolver ?? throw new ArgumentNullException("typeResolver"); this.includeNonPublicProperties = includeNonPublicProperties; } private static bool IsValidProperty(PropertyInfo property) { if (property.CanWrite) { return property.GetSetMethod(nonPublic: true).GetParameters().Length == 1; } return false; } public override IEnumerable GetProperties(Type type, object? container) { return type.GetProperties(includeNonPublicProperties).Where(IsValidProperty).Select((Func)((PropertyInfo p) => new ReflectionPropertyDescriptor(p, typeResolver))) .ToArray(); } } } namespace YamlDotNet.Serialization.Schemas { internal sealed class FailsafeSchema { public static class Tags { public static readonly TagName Map = new TagName("tag:yaml.org,2002:map"); public static readonly TagName Seq = new TagName("tag:yaml.org,2002:seq"); public static readonly TagName Str = new TagName("tag:yaml.org,2002:str"); } } internal sealed class JsonSchema { public static class Tags { public static readonly TagName Null = new TagName("tag:yaml.org,2002:null"); public static readonly TagName Bool = new TagName("tag:yaml.org,2002:bool"); public static readonly TagName Int = new TagName("tag:yaml.org,2002:int"); public static readonly TagName Float = new TagName("tag:yaml.org,2002:float"); } } internal sealed class CoreSchema { public static class Tags { } } internal sealed class DefaultSchema { public static class Tags { public static readonly TagName Timestamp = new TagName("tag:yaml.org,2002:timestamp"); } } } namespace YamlDotNet.Serialization.ObjectGraphVisitors { internal sealed class AnchorAssigner : PreProcessingPhaseObjectGraphVisitorSkeleton, IAliasProvider { private class AnchorAssignment { public AnchorName Anchor; } private readonly Dictionary assignments = new Dictionary(); private uint nextId; public AnchorAssigner(IEnumerable typeConverters) : base(typeConverters) { } protected override bool Enter(IObjectDescriptor value, ObjectSerializer serializer) { if (value.Value != null && assignments.TryGetValue(value.Value, out AnchorAssignment value2)) { if (value2.Anchor.IsEmpty) { value2.Anchor = new AnchorName("o" + nextId.ToString(CultureInfo.InvariantCulture)); nextId++; } return false; } return true; } protected override bool EnterMapping(IObjectDescriptor key, IObjectDescriptor value, ObjectSerializer serializer) { return true; } protected override bool EnterMapping(IPropertyDescriptor key, IObjectDescriptor value, ObjectSerializer serializer) { return true; } protected override void VisitScalar(IObjectDescriptor scalar, ObjectSerializer serializer) { } protected override void VisitMappingStart(IObjectDescriptor mapping, Type keyType, Type valueType, ObjectSerializer serializer) { VisitObject(mapping); } protected override void VisitMappingEnd(IObjectDescriptor mapping, ObjectSerializer serializer) { } protected override void VisitSequenceStart(IObjectDescriptor sequence, Type elementType, ObjectSerializer serializer) { VisitObject(sequence); } protected override void VisitSequenceEnd(IObjectDescriptor sequence, ObjectSerializer serializer) { } private void VisitObject(IObjectDescriptor value) { if (value.Value != null) { assignments.Add(value.Value, new AnchorAssignment()); } } AnchorName IAliasProvider.GetAlias(object target) { if (target != null && assignments.TryGetValue(target, out AnchorAssignment value)) { return value.Anchor; } return AnchorName.Empty; } } internal sealed class AnchorAssigningObjectGraphVisitor : ChainedObjectGraphVisitor { private readonly IEventEmitter eventEmitter; private readonly IAliasProvider aliasProvider; private readonly HashSet emittedAliases = new HashSet(); public AnchorAssigningObjectGraphVisitor(IObjectGraphVisitor nextVisitor, IEventEmitter eventEmitter, IAliasProvider aliasProvider) : base(nextVisitor) { this.eventEmitter = eventEmitter; this.aliasProvider = aliasProvider; } public override bool Enter(IPropertyDescriptor? propertyDescriptor, IObjectDescriptor value, IEmitter context, ObjectSerializer serializer) { if (value.Value != null) { AnchorName alias = aliasProvider.GetAlias(value.Value); if (!alias.IsEmpty && !emittedAliases.Add(alias)) { AliasEventInfo aliasEventInfo = new AliasEventInfo(value, alias); eventEmitter.Emit(aliasEventInfo, context); return aliasEventInfo.NeedsExpansion; } } return base.Enter(propertyDescriptor, value, context, serializer); } public override void VisitMappingStart(IObjectDescriptor mapping, Type keyType, Type valueType, IEmitter context, ObjectSerializer serializer) { AnchorName alias = aliasProvider.GetAlias(mapping.NonNullValue()); eventEmitter.Emit(new MappingStartEventInfo(mapping) { Anchor = alias }, context); } public override void VisitSequenceStart(IObjectDescriptor sequence, Type elementType, IEmitter context, ObjectSerializer serializer) { AnchorName alias = aliasProvider.GetAlias(sequence.NonNullValue()); eventEmitter.Emit(new SequenceStartEventInfo(sequence) { Anchor = alias }, context); } public override void VisitScalar(IObjectDescriptor scalar, IEmitter context, ObjectSerializer serializer) { ScalarEventInfo scalarEventInfo = new ScalarEventInfo(scalar); if (scalar.Value != null) { scalarEventInfo.Anchor = aliasProvider.GetAlias(scalar.Value); } eventEmitter.Emit(scalarEventInfo, context); } } internal abstract class ChainedObjectGraphVisitor : IObjectGraphVisitor { private readonly IObjectGraphVisitor nextVisitor; protected ChainedObjectGraphVisitor(IObjectGraphVisitor nextVisitor) { this.nextVisitor = nextVisitor; } public virtual bool Enter(IPropertyDescriptor? propertyDescriptor, IObjectDescriptor value, IEmitter context, ObjectSerializer serializer) { return nextVisitor.Enter(propertyDescriptor, value, context, serializer); } public virtual bool EnterMapping(IObjectDescriptor key, IObjectDescriptor value, IEmitter context, ObjectSerializer serializer) { return nextVisitor.EnterMapping(key, value, context, serializer); } public virtual bool EnterMapping(IPropertyDescriptor key, IObjectDescriptor value, IEmitter context, ObjectSerializer serializer) { return nextVisitor.EnterMapping(key, value, context, serializer); } public virtual void VisitScalar(IObjectDescriptor scalar, IEmitter context, ObjectSerializer serializer) { nextVisitor.VisitScalar(scalar, context, serializer); } public virtual void VisitMappingStart(IObjectDescriptor mapping, Type keyType, Type valueType, IEmitter context, ObjectSerializer serializer) { nextVisitor.VisitMappingStart(mapping, keyType, valueType, context, serializer); } public virtual void VisitMappingEnd(IObjectDescriptor mapping, IEmitter context, ObjectSerializer serializer) { nextVisitor.VisitMappingEnd(mapping, context, serializer); } public virtual void VisitSequenceStart(IObjectDescriptor sequence, Type elementType, IEmitter context, ObjectSerializer serializer) { nextVisitor.VisitSequenceStart(sequence, elementType, context, serializer); } public virtual void VisitSequenceEnd(IObjectDescriptor sequence, IEmitter context, ObjectSerializer serializer) { nextVisitor.VisitSequenceEnd(sequence, context, serializer); } } internal sealed class CommentsObjectGraphVisitor : ChainedObjectGraphVisitor { public CommentsObjectGraphVisitor(IObjectGraphVisitor nextVisitor) : base(nextVisitor) { } public override bool EnterMapping(IPropertyDescriptor key, IObjectDescriptor value, IEmitter context, ObjectSerializer serializer) { YamlMemberAttribute customAttribute = key.GetCustomAttribute(); if (customAttribute != null && customAttribute.Description != null) { context.Emit(new YamlDotNet.Core.Events.Comment(customAttribute.Description, isInline: false)); } return base.EnterMapping(key, value, context, serializer); } } internal sealed class CustomSerializationObjectGraphVisitor : ChainedObjectGraphVisitor { private readonly TypeConverterCache typeConverters; private readonly ObjectSerializer nestedObjectSerializer; public CustomSerializationObjectGraphVisitor(IObjectGraphVisitor nextVisitor, IEnumerable typeConverters, ObjectSerializer nestedObjectSerializer) : base(nextVisitor) { this.typeConverters = new TypeConverterCache(typeConverters); this.nestedObjectSerializer = nestedObjectSerializer; } public override bool Enter(IPropertyDescriptor? propertyDescriptor, IObjectDescriptor value, IEmitter context, ObjectSerializer serializer) { if (propertyDescriptor?.ConverterType != null) { IYamlTypeConverter converterByType = typeConverters.GetConverterByType(propertyDescriptor.ConverterType); converterByType.WriteYaml(context, value.Value, value.Type, serializer); return false; } if (typeConverters.TryGetConverterForType(value.Type, out IYamlTypeConverter typeConverter)) { typeConverter.WriteYaml(context, value.Value, value.Type, serializer); return false; } if (value.Value is IYamlConvertible yamlConvertible) { yamlConvertible.Write(context, nestedObjectSerializer); return false; } if (value.Value is IYamlSerializable yamlSerializable) { yamlSerializable.WriteYaml(context); return false; } return base.Enter(propertyDescriptor, value, context, serializer); } } internal sealed class DefaultExclusiveObjectGraphVisitor : ChainedObjectGraphVisitor { public DefaultExclusiveObjectGraphVisitor(IObjectGraphVisitor nextVisitor) : base(nextVisitor) { } private static object? GetDefault(Type type) { if (!type.IsValueType()) { return null; } return Activator.CreateInstance(type); } public override bool EnterMapping(IObjectDescriptor key, IObjectDescriptor value, IEmitter context, ObjectSerializer serializer) { if (!object.Equals(value.Value, GetDefault(value.Type))) { return base.EnterMapping(key, value, context, serializer); } return false; } public override bool EnterMapping(IPropertyDescriptor key, IObjectDescriptor value, IEmitter context, ObjectSerializer serializer) { DefaultValueAttribute customAttribute = key.GetCustomAttribute(); object objB = ((customAttribute != null) ? customAttribute.Value : GetDefault(key.Type)); if (!object.Equals(value.Value, objB)) { return base.EnterMapping(key, value, context, serializer); } return false; } } internal sealed class DefaultValuesObjectGraphVisitor : ChainedObjectGraphVisitor { private readonly DefaultValuesHandling handling; private readonly IObjectFactory factory; public DefaultValuesObjectGraphVisitor(DefaultValuesHandling handling, IObjectGraphVisitor nextVisitor, IObjectFactory factory) : base(nextVisitor) { this.handling = handling; this.factory = factory; } private object? GetDefault(Type type) { return factory.CreatePrimitive(type); } public override bool EnterMapping(IPropertyDescriptor key, IObjectDescriptor value, IEmitter context, ObjectSerializer serializer) { DefaultValuesHandling defaultValuesHandling = handling; YamlMemberAttribute customAttribute = key.GetCustomAttribute(); if (customAttribute != null && customAttribute.IsDefaultValuesHandlingSpecified) { defaultValuesHandling = customAttribute.DefaultValuesHandling; } if ((defaultValuesHandling & DefaultValuesHandling.OmitNull) != DefaultValuesHandling.Preserve && value.Value == null) { return false; } if ((defaultValuesHandling & DefaultValuesHandling.OmitEmptyCollections) != DefaultValuesHandling.Preserve && value.Value is IEnumerable enumerable) { IEnumerator enumerator = enumerable.GetEnumerator(); bool flag = enumerator.MoveNext(); if (enumerator is IDisposable disposable) { disposable.Dispose(); } if (!flag) { return false; } } if ((defaultValuesHandling & DefaultValuesHandling.OmitDefaults) != DefaultValuesHandling.Preserve) { object objB = key.GetCustomAttribute()?.Value ?? GetDefault(key.Type); if (object.Equals(value.Value, objB)) { return false; } } return base.EnterMapping(key, value, context, serializer); } } internal sealed class EmittingObjectGraphVisitor : IObjectGraphVisitor { private readonly IEventEmitter eventEmitter; public EmittingObjectGraphVisitor(IEventEmitter eventEmitter) { this.eventEmitter = eventEmitter; } bool IObjectGraphVisitor.Enter(IPropertyDescriptor? propertyDescriptor, IObjectDescriptor value, IEmitter context, ObjectSerializer serializer) { return true; } bool IObjectGraphVisitor.EnterMapping(IObjectDescriptor key, IObjectDescriptor value, IEmitter context, ObjectSerializer serializer) { return true; } bool IObjectGraphVisitor.EnterMapping(IPropertyDescriptor key, IObjectDescriptor value, IEmitter context, ObjectSerializer serializer) { return true; } void IObjectGraphVisitor.VisitScalar(IObjectDescriptor scalar, IEmitter context, ObjectSerializer serializer) { eventEmitter.Emit(new ScalarEventInfo(scalar), context); } void IObjectGraphVisitor.VisitMappingStart(IObjectDescriptor mapping, Type keyType, Type valueType, IEmitter context, ObjectSerializer serializer) { eventEmitter.Emit(new MappingStartEventInfo(mapping), context); } void IObjectGraphVisitor.VisitMappingEnd(IObjectDescriptor mapping, IEmitter context, ObjectSerializer serializer) { eventEmitter.Emit(new MappingEndEventInfo(mapping), context); } void IObjectGraphVisitor.VisitSequenceStart(IObjectDescriptor sequence, Type elementType, IEmitter context, ObjectSerializer serializer) { eventEmitter.Emit(new SequenceStartEventInfo(sequence), context); } void IObjectGraphVisitor.VisitSequenceEnd(IObjectDescriptor sequence, IEmitter context, ObjectSerializer serializer) { eventEmitter.Emit(new SequenceEndEventInfo(sequence), context); } } internal abstract class PreProcessingPhaseObjectGraphVisitorSkeleton : IObjectGraphVisitor { protected readonly IEnumerable typeConverters; private readonly TypeConverterCache typeConverterCache; public PreProcessingPhaseObjectGraphVisitorSkeleton(IEnumerable typeConverters) { typeConverterCache = new TypeConverterCache((IYamlTypeConverter[])(this.typeConverters = typeConverters?.ToArray() ?? Array.Empty())); } bool IObjectGraphVisitor.Enter(IPropertyDescriptor? propertyDescriptor, IObjectDescriptor value, Nothing context, ObjectSerializer serializer) { if (typeConverterCache.TryGetConverterForType(value.Type, out IYamlTypeConverter _)) { return false; } if (value.Value is IYamlConvertible) { return false; } if (value.Value is IYamlSerializable) { return false; } return Enter(value, serializer); } bool IObjectGraphVisitor.EnterMapping(IPropertyDescriptor key, IObjectDescriptor value, Nothing context, ObjectSerializer serializer) { return EnterMapping(key, value, serializer); } bool IObjectGraphVisitor.EnterMapping(IObjectDescriptor key, IObjectDescriptor value, Nothing context, ObjectSerializer serializer) { return EnterMapping(key, value, serializer); } void IObjectGraphVisitor.VisitMappingEnd(IObjectDescriptor mapping, Nothing context, ObjectSerializer serializer) { VisitMappingEnd(mapping, serializer); } void IObjectGraphVisitor.VisitMappingStart(IObjectDescriptor mapping, Type keyType, Type valueType, Nothing context, ObjectSerializer serializer) { VisitMappingStart(mapping, keyType, valueType, serializer); } void IObjectGraphVisitor.VisitScalar(IObjectDescriptor scalar, Nothing context, ObjectSerializer serializer) { VisitScalar(scalar, serializer); } void IObjectGraphVisitor.VisitSequenceEnd(IObjectDescriptor sequence, Nothing context, ObjectSerializer serializer) { VisitSequenceEnd(sequence, serializer); } void IObjectGraphVisitor.VisitSequenceStart(IObjectDescriptor sequence, Type elementType, Nothing context, ObjectSerializer serializer) { VisitSequenceStart(sequence, elementType, serializer); } protected abstract bool Enter(IObjectDescriptor value, ObjectSerializer serializer); protected abstract bool EnterMapping(IPropertyDescriptor key, IObjectDescriptor value, ObjectSerializer serializer); protected abstract bool EnterMapping(IObjectDescriptor key, IObjectDescriptor value, ObjectSerializer serializer); protected abstract void VisitMappingEnd(IObjectDescriptor mapping, ObjectSerializer serializer); protected abstract void VisitMappingStart(IObjectDescriptor mapping, Type keyType, Type valueType, ObjectSerializer serializer); protected abstract void VisitScalar(IObjectDescriptor scalar, ObjectSerializer serializer); protected abstract void VisitSequenceEnd(IObjectDescriptor sequence, ObjectSerializer serializer); protected abstract void VisitSequenceStart(IObjectDescriptor sequence, Type elementType, ObjectSerializer serializer); } } namespace YamlDotNet.Serialization.ObjectGraphTraversalStrategies { internal class FullObjectGraphTraversalStrategy : IObjectGraphTraversalStrategy { protected readonly struct ObjectPathSegment { public readonly object Name; public readonly IObjectDescriptor Value; public ObjectPathSegment(object name, IObjectDescriptor value) { Name = name; Value = value; } } private readonly int maxRecursion; private readonly ITypeInspector typeDescriptor; private readonly ITypeResolver typeResolver; private readonly INamingConvention namingConvention; private readonly IObjectFactory objectFactory; public FullObjectGraphTraversalStrategy(ITypeInspector typeDescriptor, ITypeResolver typeResolver, int maxRecursion, INamingConvention namingConvention, IObjectFactory objectFactory) { if (maxRecursion <= 0) { throw new ArgumentOutOfRangeException("maxRecursion", maxRecursion, "maxRecursion must be greater than 1"); } this.typeDescriptor = typeDescriptor ?? throw new ArgumentNullException("typeDescriptor"); this.typeResolver = typeResolver ?? throw new ArgumentNullException("typeResolver"); this.maxRecursion = maxRecursion; this.namingConvention = namingConvention ?? throw new ArgumentNullException("namingConvention"); this.objectFactory = objectFactory ?? throw new ArgumentNullException("objectFactory"); } void IObjectGraphTraversalStrategy.Traverse(IObjectDescriptor graph, IObjectGraphVisitor visitor, TContext context, ObjectSerializer serializer) { Traverse(null, "", graph, visitor, context, new Stack(maxRecursion), serializer); } protected virtual void Traverse(IPropertyDescriptor? propertyDescriptor, object name, IObjectDescriptor value, IObjectGraphVisitor visitor, TContext context, Stack path, ObjectSerializer serializer) { if (path.Count >= maxRecursion) { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine("Too much recursion when traversing the object graph."); stringBuilder.AppendLine("The path to reach this recursion was:"); Stack> stack = new Stack>(path.Count); int num = 0; foreach (ObjectPathSegment item in path) { string text = item.Name?.ToString() ?? string.Empty; num = Math.Max(num, text.Length); stack.Push(new KeyValuePair(text, item.Value.Type.FullName)); } foreach (KeyValuePair item2 in stack) { stringBuilder.Append(" -> ").Append(item2.Key.PadRight(num)).Append(" [") .Append(item2.Value) .AppendLine("]"); } throw new MaximumRecursionLevelReachedException(stringBuilder.ToString()); } if (!visitor.Enter(propertyDescriptor, value, context, serializer)) { return; } path.Push(new ObjectPathSegment(name, value)); try { TypeCode typeCode = value.Type.GetTypeCode(); switch (typeCode) { case TypeCode.Boolean: case TypeCode.Char: case TypeCode.SByte: case TypeCode.Byte: case TypeCode.Int16: case TypeCode.UInt16: case TypeCode.Int32: case TypeCode.UInt32: case TypeCode.Int64: case TypeCode.UInt64: case TypeCode.Single: case TypeCode.Double: case TypeCode.Decimal: case TypeCode.DateTime: case TypeCode.String: visitor.VisitScalar(value, context, serializer); return; case TypeCode.Empty: throw new NotSupportedException($"TypeCode.{typeCode} is not supported."); } if (value.IsDbNull()) { visitor.VisitScalar(new ObjectDescriptor(null, typeof(object), typeof(object)), context, serializer); } if (value.Value == null || value.Type == typeof(TimeSpan)) { visitor.VisitScalar(value, context, serializer); return; } Type underlyingType = Nullable.GetUnderlyingType(value.Type); Type type = underlyingType ?? FsharpHelper.GetOptionUnderlyingType(value.Type); object obj = ((type != null) ? FsharpHelper.GetValue(value) : null); if (underlyingType != null) { Traverse(propertyDescriptor, "Value", new ObjectDescriptor(value.Value, underlyingType, value.Type, value.ScalarStyle), visitor, context, path, serializer); } else if (type != null && obj != null) { Traverse(propertyDescriptor, "Value", new ObjectDescriptor(FsharpHelper.GetValue(value), type, value.Type, value.ScalarStyle), visitor, context, path, serializer); } else { TraverseObject(propertyDescriptor, value, visitor, context, path, serializer); } } finally { path.Pop(); } } protected virtual void TraverseObject(IPropertyDescriptor? propertyDescriptor, IObjectDescriptor value, IObjectGraphVisitor visitor, TContext context, Stack path, ObjectSerializer serializer) { IDictionary dictionary; Type[] genericArguments; if (typeof(IDictionary).IsAssignableFrom(value.Type)) { TraverseDictionary(propertyDescriptor, value, visitor, typeof(object), typeof(object), context, path, serializer); } else if (objectFactory.GetDictionary(value, out dictionary, out genericArguments)) { TraverseDictionary(propertyDescriptor, new ObjectDescriptor(dictionary, value.Type, value.StaticType, value.ScalarStyle), visitor, genericArguments[0], genericArguments[1], context, path, serializer); } else if (typeof(IEnumerable).IsAssignableFrom(value.Type)) { TraverseList(propertyDescriptor, value, visitor, context, path, serializer); } else { TraverseProperties(value, visitor, context, path, serializer); } } protected virtual void TraverseDictionary(IPropertyDescriptor? propertyDescriptor, IObjectDescriptor dictionary, IObjectGraphVisitor visitor, Type keyType, Type valueType, TContext context, Stack path, ObjectSerializer serializer) { visitor.VisitMappingStart(dictionary, keyType, valueType, context, serializer); bool flag = dictionary.Type.FullName.Equals("System.Dynamic.ExpandoObject"); foreach (DictionaryEntry? item in (IDictionary)dictionary.NonNullValue()) { DictionaryEntry value = item.Value; object obj = (flag ? namingConvention.Apply(value.Key.ToString()) : value.Key); ObjectDescriptor objectDescriptor = GetObjectDescriptor(obj, keyType); ObjectDescriptor objectDescriptor2 = GetObjectDescriptor(value.Value, valueType); if (visitor.EnterMapping(objectDescriptor, objectDescriptor2, context, serializer)) { Traverse(propertyDescriptor, obj, objectDescriptor, visitor, context, path, serializer); Traverse(propertyDescriptor, obj, objectDescriptor2, visitor, context, path, serializer); } } visitor.VisitMappingEnd(dictionary, context, serializer); } private void TraverseList(IPropertyDescriptor propertyDescriptor, IObjectDescriptor value, IObjectGraphVisitor visitor, TContext context, Stack path, ObjectSerializer serializer) { Type valueType = objectFactory.GetValueType(value.Type); visitor.VisitSequenceStart(value, valueType, context, serializer); int num = 0; foreach (object item in (IEnumerable)value.NonNullValue()) { Traverse(propertyDescriptor, num, GetObjectDescriptor(item, valueType), visitor, context, path, serializer); num++; } visitor.VisitSequenceEnd(value, context, serializer); } protected virtual void TraverseProperties(IObjectDescriptor value, IObjectGraphVisitor visitor, TContext context, Stack path, ObjectSerializer serializer) { if (context.GetType() != typeof(Nothing)) { objectFactory.ExecuteOnSerializing(value.Value); } visitor.VisitMappingStart(value, typeof(string), typeof(object), context, serializer); object obj = value.NonNullValue(); foreach (IPropertyDescriptor property in typeDescriptor.GetProperties(value.Type, obj)) { IObjectDescriptor value2 = property.Read(obj); if (visitor.EnterMapping(property, value2, context, serializer)) { Traverse(null, property.Name, new ObjectDescriptor(property.Name, typeof(string), typeof(string), ScalarStyle.Plain), visitor, context, path, serializer); Traverse(property, property.Name, value2, visitor, context, path, serializer); } } visitor.VisitMappingEnd(value, context, serializer); if (context.GetType() != typeof(Nothing)) { objectFactory.ExecuteOnSerialized(value.Value); } } private ObjectDescriptor GetObjectDescriptor(object? value, Type staticType) { return new ObjectDescriptor(value, typeResolver.Resolve(staticType, value), staticType); } } internal class RoundtripObjectGraphTraversalStrategy : FullObjectGraphTraversalStrategy { private readonly TypeConverterCache converters; private readonly Settings settings; public RoundtripObjectGraphTraversalStrategy(IEnumerable converters, ITypeInspector typeDescriptor, ITypeResolver typeResolver, int maxRecursion, INamingConvention namingConvention, Settings settings, IObjectFactory factory) : base(typeDescriptor, typeResolver, maxRecursion, namingConvention, factory) { this.converters = new TypeConverterCache(converters); this.settings = settings; } protected override void TraverseProperties(IObjectDescriptor value, IObjectGraphVisitor visitor, TContext context, Stack path, ObjectSerializer serializer) { if (!value.Type.HasDefaultConstructor(settings.AllowPrivateConstructors) && !converters.TryGetConverterForType(value.Type, out IYamlTypeConverter _)) { throw new InvalidOperationException($"Type '{value.Type}' cannot be deserialized because it does not have a default constructor or a type converter."); } base.TraverseProperties(value, visitor, context, path, serializer); } } } namespace YamlDotNet.Serialization.ObjectFactories { internal class DefaultObjectFactory : ObjectFactoryBase { private readonly Dictionary> stateMethods = new Dictionary> { { typeof(YamlDotNet.Serialization.Callbacks.OnDeserializedAttribute), new ConcurrentDictionary() }, { typeof(YamlDotNet.Serialization.Callbacks.OnDeserializingAttribute), new ConcurrentDictionary() }, { typeof(YamlDotNet.Serialization.Callbacks.OnSerializedAttribute), new ConcurrentDictionary() }, { typeof(YamlDotNet.Serialization.Callbacks.OnSerializingAttribute), new ConcurrentDictionary() } }; private readonly Dictionary defaultGenericInterfaceImplementations = new Dictionary { { typeof(IEnumerable<>), typeof(List<>) }, { typeof(ICollection<>), typeof(List<>) }, { typeof(IList<>), typeof(List<>) }, { typeof(IDictionary<, >), typeof(Dictionary<, >) } }; private readonly Dictionary defaultNonGenericInterfaceImplementations = new Dictionary { { typeof(IEnumerable), typeof(List) }, { typeof(ICollection), typeof(List) }, { typeof(IList), typeof(List) }, { typeof(IDictionary), typeof(Dictionary) } }; private readonly Settings settings; public DefaultObjectFactory() : this(new Dictionary(), new Settings()) { } public DefaultObjectFactory(IDictionary mappings) : this(mappings, new Settings()) { } public DefaultObjectFactory(IDictionary mappings, Settings settings) { foreach (KeyValuePair mapping in mappings) { if (!mapping.Key.IsAssignableFrom(mapping.Value)) { throw new InvalidOperationException($"Type '{mapping.Value}' does not implement type '{mapping.Key}'."); } defaultNonGenericInterfaceImplementations.Add(mapping.Key, mapping.Value); } this.settings = settings; } public override object Create(Type type) { if (type.IsInterface()) { Type value2; if (type.IsGenericType()) { if (defaultGenericInterfaceImplementations.TryGetValue(type.GetGenericTypeDefinition(), out Type value)) { type = value.MakeGenericType(type.GetGenericArguments()); } } else if (defaultNonGenericInterfaceImplementations.TryGetValue(type, out value2)) { type = value2; } } try { return Activator.CreateInstance(type, settings.AllowPrivateConstructors); } catch (Exception innerException) { string message = "Failed to create an instance of type '" + type.FullName + "'."; throw new InvalidOperationException(message, innerException); } } public override void ExecuteOnDeserialized(object value) { ExecuteState(typeof(YamlDotNet.Serialization.Callbacks.OnDeserializedAttribute), value); } public override void ExecuteOnDeserializing(object value) { ExecuteState(typeof(YamlDotNet.Serialization.Callbacks.OnDeserializingAttribute), value); } public override void ExecuteOnSerialized(object value) { ExecuteState(typeof(YamlDotNet.Serialization.Callbacks.OnSerializedAttribute), value); } public override void ExecuteOnSerializing(object value) { ExecuteState(typeof(YamlDotNet.Serialization.Callbacks.OnSerializingAttribute), value); } private void ExecuteState(Type attributeType, object value) { if (value != null) { Type type = value.GetType(); MethodInfo[] array = GetStateMethods(attributeType, type); MethodInfo[] array2 = array; foreach (MethodInfo methodInfo in array2) { methodInfo.Invoke(value, null); } } } private MethodInfo[] GetStateMethods(Type attributeType, Type valueType) { ConcurrentDictionary concurrentDictionary = stateMethods[attributeType]; return concurrentDictionary.GetOrAdd(valueType, delegate(Type type) { MethodInfo[] methods = type.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); return methods.Where((MethodInfo x) => x.GetCustomAttributes(attributeType, inherit: true).Length != 0).ToArray(); }); } } internal sealed class LambdaObjectFactory : ObjectFactoryBase { private readonly Func factory; public LambdaObjectFactory(Func factory) { this.factory = factory ?? throw new ArgumentNullException("factory"); } public override object Create(Type type) { return factory(type); } } internal abstract class ObjectFactoryBase : IObjectFactory { public abstract object Create(Type type); public virtual object? CreatePrimitive(Type type) { if (!type.IsValueType()) { return null; } return Activator.CreateInstance(type); } public virtual void ExecuteOnDeserialized(object value) { } public virtual void ExecuteOnDeserializing(object value) { } public virtual void ExecuteOnSerialized(object value) { } public virtual void ExecuteOnSerializing(object value) { } public virtual bool GetDictionary(IObjectDescriptor descriptor, out IDictionary? dictionary, out Type[]? genericArguments) { Type implementationOfOpenGenericInterface = descriptor.Type.GetImplementationOfOpenGenericInterface(typeof(IDictionary<, >)); if (implementationOfOpenGenericInterface != null) { genericArguments = implementationOfOpenGenericInterface.GetGenericArguments(); object obj = Activator.CreateInstance(typeof(GenericDictionaryToNonGenericAdapter<, >).MakeGenericType(genericArguments), descriptor.Value); dictionary = obj as IDictionary; return true; } genericArguments = null; dictionary = null; return false; } public virtual Type GetValueType(Type type) { Type implementationOfOpenGenericInterface = type.GetImplementationOfOpenGenericInterface(typeof(IEnumerable<>)); return (implementationOfOpenGenericInterface != null) ? implementationOfOpenGenericInterface.GetGenericArguments()[0] : typeof(object); } } internal abstract class StaticObjectFactory : IObjectFactory { public abstract object Create(Type type); public abstract Array CreateArray(Type type, int count); public abstract bool IsDictionary(Type type); public abstract bool IsArray(Type type); public abstract bool IsList(Type type); public abstract Type GetKeyType(Type type); public abstract Type GetValueType(Type type); public virtual object? CreatePrimitive(Type type) { return Type.GetTypeCode(type) switch { TypeCode.Boolean => false, TypeCode.Byte => (byte)0, TypeCode.Int16 => (short)0, TypeCode.Int32 => 0, TypeCode.Int64 => 0L, TypeCode.SByte => (sbyte)0, TypeCode.UInt16 => (ushort)0, TypeCode.UInt32 => 0u, TypeCode.UInt64 => 0uL, TypeCode.Single => 0f, TypeCode.Double => 0.0, TypeCode.Decimal => 0m, TypeCode.Char => '\0', TypeCode.DateTime => default(DateTime), _ => null, }; } public bool GetDictionary(IObjectDescriptor descriptor, out IDictionary? dictionary, out Type[]? genericArguments) { dictionary = null; genericArguments = null; return false; } public abstract void ExecuteOnDeserializing(object value); public abstract void ExecuteOnDeserialized(object value); public abstract void ExecuteOnSerializing(object value); public abstract void ExecuteOnSerialized(object value); } } namespace YamlDotNet.Serialization.NodeTypeResolvers { internal sealed class DefaultContainersNodeTypeResolver : INodeTypeResolver { bool INodeTypeResolver.Resolve(NodeEvent? nodeEvent, ref Type currentType) { if (currentType == typeof(object)) { if (nodeEvent is SequenceStart) { currentType = typeof(List); return true; } if (nodeEvent is MappingStart) { currentType = typeof(Dictionary); return true; } } return false; } } internal class MappingNodeTypeResolver : INodeTypeResolver { private readonly IDictionary mappings; public MappingNodeTypeResolver(IDictionary mappings) { if (mappings == null) { throw new ArgumentNullException("mappings"); } foreach (KeyValuePair mapping in mappings) { if (!mapping.Key.IsAssignableFrom(mapping.Value)) { throw new InvalidOperationException($"Type '{mapping.Value}' does not implement type '{mapping.Key}'."); } } this.mappings = mappings; } public bool Resolve(NodeEvent? nodeEvent, ref Type currentType) { if (mappings.TryGetValue(currentType, out Type value)) { currentType = value; return true; } return false; } } internal class PreventUnknownTagsNodeTypeResolver : INodeTypeResolver { bool INodeTypeResolver.Resolve(NodeEvent? nodeEvent, ref Type currentType) { if (nodeEvent != null && !nodeEvent.Tag.IsEmpty) { throw new YamlException(nodeEvent.Start, nodeEvent.End, $"Encountered an unresolved tag '{nodeEvent.Tag}'"); } return false; } } internal sealed class TagNodeTypeResolver : INodeTypeResolver { private readonly IDictionary tagMappings; public TagNodeTypeResolver(IDictionary tagMappings) { this.tagMappings = tagMappings ?? throw new ArgumentNullException("tagMappings"); } bool INodeTypeResolver.Resolve(NodeEvent? nodeEvent, ref Type currentType) { if (nodeEvent != null && !nodeEvent.Tag.IsEmpty && tagMappings.TryGetValue(nodeEvent.Tag, out Type value)) { currentType = value; return true; } return false; } } [Obsolete("The mechanism that this class uses to specify type names is non-standard. Register the tags explicitly instead of using this convention.")] internal sealed class TypeNameInTagNodeTypeResolver : INodeTypeResolver { bool INodeTypeResolver.Resolve(NodeEvent? nodeEvent, ref Type currentType) { if (nodeEvent != null && !nodeEvent.Tag.IsEmpty) { Type type = Type.GetType(nodeEvent.Tag.Value.Substring(1), throwOnError: false); if (type != null) { currentType = type; return true; } } return false; } } internal sealed class YamlConvertibleTypeResolver : INodeTypeResolver { public bool Resolve(NodeEvent? nodeEvent, ref Type currentType) { return typeof(IYamlConvertible).IsAssignableFrom(currentType); } } internal sealed class YamlSerializableTypeResolver : INodeTypeResolver { public bool Resolve(NodeEvent? nodeEvent, ref Type currentType) { return typeof(IYamlSerializable).IsAssignableFrom(currentType); } } } namespace YamlDotNet.Serialization.NodeDeserializers { internal sealed class ArrayNodeDeserializer : INodeDeserializer { private sealed class ArrayList : IList, ICollection, IEnumerable { private object?[] data; public bool IsFixedSize => false; public bool IsReadOnly => false; public object? this[int index] { get { return data[index]; } set { data[index] = value; } } public int Count { get; private set; } public bool IsSynchronized => false; public object SyncRoot => data; public ArrayList() { Clear(); } public int Add(object? value) { if (Count == data.Length) { Array.Resize(ref data, data.Length * 2); } data[Count] = value; return Count++; } public void Clear() { data = new object[10]; Count = 0; } bool IList.Contains(object? value) { throw new NotSupportedException(); } int IList.IndexOf(object? value) { throw new NotSupportedException(); } void IList.Insert(int index, object? value) { throw new NotSupportedException(); } void IList.Remove(object? value) { throw new NotSupportedException(); } void IList.RemoveAt(int index) { throw new NotSupportedException(); } public void CopyTo(Array array, int index) { Array.Copy(data, 0, array, index, Count); } public IEnumerator GetEnumerator() { int i = 0; while (i < Count) { yield return data[i]; int num = i + 1; i = num; } } } private readonly INamingConvention enumNamingConvention; private readonly ITypeInspector typeInspector; public ArrayNodeDeserializer(INamingConvention enumNamingConvention, ITypeInspector typeInspector) { this.enumNamingConvention = enumNamingConvention; this.typeInspector = typeInspector; } public bool Deserialize(IParser parser, Type expectedType, Func nestedObjectDeserializer, out object? value, ObjectDeserializer rootDeserializer) { if (!expectedType.IsArray) { value = false; return false; } Type itemType = expectedType.GetElementType(); ArrayList arrayList = new ArrayList(); Array array = null; CollectionNodeDeserializer.DeserializeHelper(itemType, parser, nestedObjectDeserializer, arrayList, canUpdate: true, enumNamingConvention, typeInspector, PromiseResolvedHandler); array = Array.CreateInstance(itemType, arrayList.Count); arrayList.CopyTo(array, 0); value = array; return true; void PromiseResolvedHandler(int index, object? value2) { if (array == null) { throw new InvalidOperationException("Destination array is still null"); } array.SetValue(YamlDotNet.Serialization.Utilities.TypeConverter.ChangeType(value2, itemType, enumNamingConvention, typeInspector), index); } } } internal abstract class CollectionDeserializer { protected static void DeserializeHelper(Type tItem, IParser parser, Func nestedObjectDeserializer, IList result, bool canUpdate, IObjectFactory objectFactory) { parser.Consume(); SequenceEnd @event; while (!parser.TryConsume(out @event)) { ParsingEvent current = parser.Current; object obj = nestedObjectDeserializer(parser, tItem); if (obj is IValuePromise valuePromise) { if (!canUpdate) { throw new ForwardAnchorNotSupportedException(current?.Start ?? Mark.Empty, current?.End ?? Mark.Empty, "Forward alias references are not allowed because this type does not implement IList<>"); } int index = result.Add(objectFactory.CreatePrimitive(tItem)); valuePromise.ValueAvailable += delegate(object? v) { result[index] = v; }; } else { result.Add(obj); } } } } internal sealed class CollectionNodeDeserializer : INodeDeserializer { private readonly IObjectFactory objectFactory; private readonly INamingConvention enumNamingConvention; private readonly ITypeInspector typeInspector; public CollectionNodeDeserializer(IObjectFactory objectFactory, INamingConvention enumNamingConvention, ITypeInspector typeInspector) { this.objectFactory = objectFactory ?? throw new ArgumentNullException("objectFactory"); this.enumNamingConvention = enumNamingConvention; this.typeInspector = typeInspector; } public bool Deserialize(IParser parser, Type expectedType, Func nestedObjectDeserializer, out object? value, ObjectDeserializer rootDeserializer) { bool canUpdate = true; Type implementationOfOpenGenericInterface = expectedType.GetImplementationOfOpenGenericInterface(typeof(ICollection<>)); Type type; IList list; if (implementationOfOpenGenericInterface != null) { Type[] genericArguments = implementationOfOpenGenericInterface.GetGenericArguments(); type = genericArguments[0]; value = objectFactory.Create(expectedType); list = value as IList; if (list == null) { Type implementationOfOpenGenericInterface2 = expectedType.GetImplementationOfOpenGenericInterface(typeof(IList<>)); canUpdate = implementationOfOpenGenericInterface2 != null; list = (IList)Activator.CreateInstance(typeof(GenericCollectionToNonGenericAdapter<>).MakeGenericType(type), value); } } else { if (!typeof(IList).IsAssignableFrom(expectedType)) { value = null; return false; } type = typeof(object); value = objectFactory.Create(expectedType); list = (IList)value; } DeserializeHelper(type, parser, nestedObjectDeserializer, list, canUpdate, enumNamingConvention, typeInspector); return true; } internal static void DeserializeHelper(Type tItem, IParser parser, Func nestedObjectDeserializer, IList result, bool canUpdate, INamingConvention enumNamingConvention, ITypeInspector typeInspector, Action? promiseResolvedHandler = null) { parser.Consume(); SequenceEnd @event; while (!parser.TryConsume(out @event)) { ParsingEvent current = parser.Current; object obj = nestedObjectDeserializer(parser, tItem); if (obj is IValuePromise valuePromise) { if (!canUpdate) { throw new ForwardAnchorNotSupportedException(current?.Start ?? Mark.Empty, current?.End ?? Mark.Empty, "Forward alias references are not allowed because this type does not implement IList<>"); } int index = result.Add(tItem.IsValueType() ? Activator.CreateInstance(tItem) : null); if (promiseResolvedHandler != null) { valuePromise.ValueAvailable += delegate(object? v) { promiseResolvedHandler(index, v); }; } else { valuePromise.ValueAvailable += delegate(object? v) { result[index] = YamlDotNet.Serialization.Utilities.TypeConverter.ChangeType(v, tItem, enumNamingConvention, typeInspector); }; } } else { result.Add(YamlDotNet.Serialization.Utilities.TypeConverter.ChangeType(obj, tItem, enumNamingConvention, typeInspector)); } } } } internal abstract class DictionaryDeserializer { private readonly bool duplicateKeyChecking; public DictionaryDeserializer(bool duplicateKeyChecking) { this.duplicateKeyChecking = duplicateKeyChecking; } private void TryAssign(IDictionary result, object key, object value, MappingStart propertyName) { if (duplicateKeyChecking && result.Contains(key)) { throw new YamlException(propertyName.Start, propertyName.End, $"Encountered duplicate key {key}"); } result[key] = value; } protected virtual void Deserialize(Type tKey, Type tValue, IParser parser, Func nestedObjectDeserializer, IDictionary result, ObjectDeserializer rootDeserializer) { MappingStart property = parser.Consume(); MappingEnd @event; while (!parser.TryConsume(out @event)) { object key = nestedObjectDeserializer(parser, tKey); object value = nestedObjectDeserializer(parser, tValue); IValuePromise valuePromise = value as IValuePromise; if (key is IValuePromise valuePromise2) { if (valuePromise == null) { valuePromise2.ValueAvailable += delegate(object? v) { result[v] = value; }; continue; } bool hasFirstPart = false; valuePromise2.ValueAvailable += delegate(object? v) { if (hasFirstPart) { TryAssign(result, v, value, property); } else { key = v; hasFirstPart = true; } }; valuePromise.ValueAvailable += delegate(object? v) { if (hasFirstPart) { TryAssign(result, key, v, property); } else { value = v; hasFirstPart = true; } }; continue; } if (key == null) { throw new ArgumentException("Empty key names are not supported yet.", "tKey"); } if (valuePromise == null) { TryAssign(result, key, value, property); continue; } valuePromise.ValueAvailable += delegate(object? v) { result[key] = v; }; } } } internal class DictionaryNodeDeserializer : DictionaryDeserializer, INodeDeserializer { private readonly IObjectFactory objectFactory; public DictionaryNodeDeserializer(IObjectFactory objectFactory, bool duplicateKeyChecking) : base(duplicateKeyChecking) { this.objectFactory = objectFactory ?? throw new ArgumentNullException("objectFactory"); } public bool Deserialize(IParser parser, Type expectedType, Func nestedObjectDeserializer, out object? value, ObjectDeserializer rootDeserializer) { Type implementationOfOpenGenericInterface = expectedType.GetImplementationOfOpenGenericInterface(typeof(IDictionary<, >)); Type type; Type type2; IDictionary dictionary; if (implementationOfOpenGenericInterface != null) { Type[] genericArguments = implementationOfOpenGenericInterface.GetGenericArguments(); type = genericArguments[0]; type2 = genericArguments[1]; value = objectFactory.Create(expectedType); dictionary = value as IDictionary; if (dictionary == null) { dictionary = (IDictionary)Activator.CreateInstance(typeof(GenericDictionaryToNonGenericAdapter<, >).MakeGenericType(type, type2), value); } } else { if (!typeof(IDictionary).IsAssignableFrom(expectedType)) { value = null; return false; } type = typeof(object); type2 = typeof(object); value = objectFactory.Create(expectedType); dictionary = (IDictionary)value; } Deserialize(type, type2, parser, nestedObjectDeserializer, dictionary, rootDeserializer); return true; } } internal sealed class EnumerableNodeDeserializer : INodeDeserializer { public bool Deserialize(IParser parser, Type expectedType, Func nestedObjectDeserializer, out object? value, ObjectDeserializer rootDeserializer) { Type type; if (expectedType == typeof(IEnumerable)) { type = typeof(object); } else { Type implementationOfOpenGenericInterface = expectedType.GetImplementationOfOpenGenericInterface(typeof(IEnumerable<>)); if (implementationOfOpenGenericInterface != expectedType) { value = null; return false; } type = implementationOfOpenGenericInterface.GetGenericArguments()[0]; } Type arg = typeof(List<>).MakeGenericType(type); value = nestedObjectDeserializer(parser, arg); return true; } } internal sealed class FsharpListNodeDeserializer : INodeDeserializer { private readonly ITypeInspector typeInspector; private readonly INamingConvention enumNamingConvention; public FsharpListNodeDeserializer(ITypeInspector typeInspector, INamingConvention enumNamingConvention) { this.typeInspector = typeInspector; this.enumNamingConvention = enumNamingConvention; } public bool Deserialize(IParser parser, Type expectedType, Func nestedObjectDeserializer, out object? value, ObjectDeserializer rootDeserializer) { if (!FsharpHelper.IsFsharpListType(expectedType)) { value = false; return false; } Type type = expectedType.GetGenericArguments()[0]; Type t = expectedType.GetGenericTypeDefinition().MakeGenericType(type); ArrayList arrayList = new ArrayList(); CollectionNodeDeserializer.DeserializeHelper(type, parser, nestedObjectDeserializer, arrayList, canUpdate: true, enumNamingConvention, typeInspector); Array array = Array.CreateInstance(type, arrayList.Count); arrayList.CopyTo(array, 0); object obj = FsharpHelper.CreateFsharpListFromArray(t, type, array); value = obj; return true; } } internal sealed class NullNodeDeserializer : INodeDeserializer { public bool Deserialize(IParser parser, Type expectedType, Func nestedObjectDeserializer, out object? value, ObjectDeserializer rootDeserializer) { value = null; if (parser.Accept(out var @event) && NodeIsNull(@event)) { parser.SkipThisAndNestedEvents(); return true; } return false; } private static bool NodeIsNull(NodeEvent nodeEvent) { if (nodeEvent.Tag == "tag:yaml.org,2002:null") { return true; } if (nodeEvent is YamlDotNet.Core.Events.Scalar { Style: ScalarStyle.Plain, IsKey: false } scalar) { string value = scalar.Value; switch (value) { default: return value == "NULL"; case "": case "~": case "null": case "Null": return true; } } return false; } } internal sealed class ObjectNodeDeserializer : INodeDeserializer { private readonly IObjectFactory objectFactory; private readonly ITypeInspector typeInspector; private readonly bool ignoreUnmatched; private readonly bool duplicateKeyChecking; private readonly ITypeConverter typeConverter; private readonly INamingConvention enumNamingConvention; private readonly bool enforceNullability; private readonly bool caseInsensitivePropertyMatching; private readonly bool enforceRequiredProperties; private readonly TypeConverterCache typeConverters; public ObjectNodeDeserializer(IObjectFactory objectFactory, ITypeInspector typeInspector, bool ignoreUnmatched, bool duplicateKeyChecking, ITypeConverter typeConverter, INamingConvention enumNamingConvention, bool enforceNullability, bool caseInsensitivePropertyMatching, bool enforceRequiredProperties, IEnumerable typeConverters) { this.objectFactory = objectFactory ?? throw new ArgumentNullException("objectFactory"); this.typeInspector = typeInspector ?? throw new ArgumentNullException("typeInspector"); this.ignoreUnmatched = ignoreUnmatched; this.duplicateKeyChecking = duplicateKeyChecking; this.typeConverter = typeConverter ?? throw new ArgumentNullException("typeConverter"); this.enumNamingConvention = enumNamingConvention ?? throw new ArgumentNullException("enumNamingConvention"); this.enforceNullability = enforceNullability; this.caseInsensitivePropertyMatching = caseInsensitivePropertyMatching; this.enforceRequiredProperties = enforceRequiredProperties; this.typeConverters = new TypeConverterCache(typeConverters); } public bool Deserialize(IParser parser, Type expectedType, Func nestedObjectDeserializer, out object? value, ObjectDeserializer rootDeserializer) { if (!parser.TryConsume(out var _)) { value = null; return false; } Type type = Nullable.GetUnderlyingType(expectedType) ?? FsharpHelper.GetOptionUnderlyingType(expectedType) ?? expectedType; value = objectFactory.Create(type); objectFactory.ExecuteOnDeserializing(value); HashSet hashSet = new HashSet(StringComparer.Ordinal); HashSet hashSet2 = new HashSet(StringComparer.Ordinal); Mark start = Mark.Empty; MappingEnd event2; while (!parser.TryConsume(out event2)) { YamlDotNet.Core.Events.Scalar propertyName = parser.Consume(); if (duplicateKeyChecking && !hashSet.Add(propertyName.Value)) { throw new YamlException(propertyName.Start, propertyName.End, "Encountered duplicate key " + propertyName.Value); } try { IPropertyDescriptor property = typeInspector.GetProperty(type, null, propertyName.Value, ignoreUnmatched, caseInsensitivePropertyMatching); if (property == null) { parser.SkipThisAndNestedEvents(); continue; } hashSet2.Add(property.Name); object obj; if (property.ConverterType != null) { IYamlTypeConverter converterByType = typeConverters.GetConverterByType(property.ConverterType); obj = converterByType.ReadYaml(parser, property.Type, rootDeserializer); } else { obj = nestedObjectDeserializer(parser, property.Type); } if (obj is IValuePromise valuePromise) { object valueRef = value; valuePromise.ValueAvailable += delegate(object? v) { object value3 = typeConverter.ChangeType(v, property.Type, enumNamingConvention, typeInspector); NullCheck(value3, property, propertyName); property.Write(valueRef, value3); }; } else { object value2 = typeConverter.ChangeType(obj, property.Type, enumNamingConvention, typeInspector); NullCheck(value2, property, propertyName); property.Write(value, value2); } } catch (SerializationException ex) { throw new YamlException(propertyName.Start, propertyName.End, ex.Message); } catch (YamlException) { throw; } catch (Exception innerException) { throw new YamlException(propertyName.Start, propertyName.End, "Exception during deserialization", innerException); } start = propertyName.End; } if (enforceRequiredProperties) { IEnumerable properties = typeInspector.GetProperties(type, value); List list = new List(); foreach (IPropertyDescriptor item in properties) { if (item.Required && !hashSet2.Contains(item.Name)) { list.Add(item.Name); } } if (list.Count > 0) { string text = string.Join(",", list); throw new YamlException(in start, in start, "Missing properties, '" + text + "' in source yaml."); } } objectFactory.ExecuteOnDeserialized(value); return true; } public void NullCheck(object value, IPropertyDescriptor property, YamlDotNet.Core.Events.Scalar propertyName) { if (enforceNullability && value == null && !property.AllowNulls) { throw new YamlException(propertyName.Start, propertyName.End, "Strict nullability enforcement error.", new NullReferenceException("Yaml value is null when target property requires non null values.")); } } } internal sealed class ScalarNodeDeserializer : INodeDeserializer { private const string BooleanTruePattern = "^(true|y|yes|on)$"; private const string BooleanFalsePattern = "^(false|n|no|off)$"; private readonly bool attemptUnknownTypeDeserialization; private readonly ITypeConverter typeConverter; private readonly ITypeInspector typeInspector; private readonly YamlFormatter formatter; private readonly INamingConvention enumNamingConvention; public ScalarNodeDeserializer(bool attemptUnknownTypeDeserialization, ITypeConverter typeConverter, ITypeInspector typeInspector, YamlFormatter formatter, INamingConvention enumNamingConvention) { this.attemptUnknownTypeDeserialization = attemptUnknownTypeDeserialization; this.typeConverter = typeConverter ?? throw new ArgumentNullException("typeConverter"); this.typeInspector = typeInspector; this.formatter = formatter; this.enumNamingConvention = enumNamingConvention; } public bool Deserialize(IParser parser, Type expectedType, Func nestedObjectDeserializer, out object? value, ObjectDeserializer rootDeserializer) { if (!parser.TryConsume(out var @event)) { value = null; return false; } Type type = Nullable.GetUnderlyingType(expectedType) ?? FsharpHelper.GetOptionUnderlyingType(expectedType) ?? expectedType; if (type.IsEnum()) { string name = enumNamingConvention.Reverse(@event.Value); name = typeInspector.GetEnumName(type, name); value = Enum.Parse(type, name, ignoreCase: true); return true; } TypeCode typeCode = type.GetTypeCode(); switch (typeCode) { case TypeCode.Boolean: value = DeserializeBooleanHelper(@event.Value); break; case TypeCode.SByte: case TypeCode.Byte: case TypeCode.Int16: case TypeCode.UInt16: case TypeCode.Int32: case TypeCode.UInt32: case TypeCode.Int64: case TypeCode.UInt64: value = DeserializeIntegerHelper(typeCode, @event.Value); break; case TypeCode.Single: value = float.Parse(@event.Value, formatter.NumberFormat); break; case TypeCode.Double: value = double.Parse(@event.Value, formatter.NumberFormat); break; case TypeCode.Decimal: value = decimal.Parse(@event.Value, formatter.NumberFormat); break; case TypeCode.String: value = @event.Value; break; case TypeCode.Char: value = @event.Value[0]; break; case TypeCode.DateTime: value = DateTime.Parse(@event.Value, CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind); break; default: if (expectedType == typeof(object)) { if (!@event.IsKey && attemptUnknownTypeDeserialization) { value = AttemptUnknownTypeDeserialization(@event); } else { value = @event.Value; } } else { value = typeConverter.ChangeType(@event.Value, expectedType, enumNamingConvention, typeInspector); } break; } return true; } private static bool DeserializeBooleanHelper(string value) { if (Regex.IsMatch(value, "^(true|y|yes|on)$", RegexOptions.IgnoreCase)) { return true; } if (Regex.IsMatch(value, "^(false|n|no|off)$", RegexOptions.IgnoreCase)) { return false; } throw new FormatException("The value \"" + value + "\" is not a valid YAML Boolean"); } private object DeserializeIntegerHelper(TypeCode typeCode, string value) { StringBuilderPool.BuilderWrapper builderWrapper = StringBuilderPool.Rent(); try { StringBuilder builder = builderWrapper.Builder; int i = 0; bool flag = false; ulong num = 0uL; if (value[0] == '-') { i++; flag = true; } else if (value[0] == '+') { i++; } if (value[i] == '0') { int num2; if (i == value.Length - 1) { num2 = 10; num = 0uL; } else { i++; if (value[i] == 'b') { num2 = 2; i++; } else if (value[i] == 'x') { num2 = 16; i++; } else { num2 = 8; } } for (; i < value.Length; i++) { if (value[i] != '_') { builder.Append(value[i]); } } switch (num2) { case 2: case 8: num = Convert.ToUInt64(builder.ToString(), num2); break; case 16: num = ulong.Parse(builder.ToString(), NumberStyles.HexNumber, formatter.NumberFormat); break; } } else { string[] array = value.Substring(i).Split(new char[1] { ':' }); num = 0uL; for (int j = 0; j < array.Length; j++) { num *= 60; num += ulong.Parse(array[j].Replace("_", ""), CultureInfo.InvariantCulture); } } if (!flag) { return CastInteger(num, typeCode); } long number = ((num != 9223372036854775808uL) ? checked(-(long)num) : long.MinValue); return CastInteger(number, typeCode); } finally { ((IDisposable)builderWrapper/*cast due to .constrained prefix*/).Dispose(); } } private static object CastInteger(long number, TypeCode typeCode) { return checked(typeCode switch { TypeCode.Byte => (byte)number, TypeCode.Int16 => (short)number, TypeCode.Int32 => (int)number, TypeCode.Int64 => number, TypeCode.SByte => (sbyte)number, TypeCode.UInt16 => (ushort)number, TypeCode.UInt32 => (uint)number, TypeCode.UInt64 => (ulong)number, _ => number, }); } private static object CastInteger(ulong number, TypeCode typeCode) { return checked(typeCode switch { TypeCode.Byte => (byte)number, TypeCode.Int16 => (short)number, TypeCode.Int32 => (int)number, TypeCode.Int64 => (long)number, TypeCode.SByte => (sbyte)number, TypeCode.UInt16 => (ushort)number, TypeCode.UInt32 => (uint)number, TypeCode.UInt64 => number, _ => number, }); } private object? AttemptUnknownTypeDeserialization(YamlDotNet.Core.Events.Scalar value) { if (value.Style == ScalarStyle.SingleQuoted || value.Style == ScalarStyle.DoubleQuoted || value.Style == ScalarStyle.Folded) { return value.Value; } string v = value.Value; switch (v) { case "null": case "Null": case "NULL": case "~": case "": return null; case "true": case "True": case "TRUE": return true; case "False": case "FALSE": case "false": return false; default: if (Regex.IsMatch(v, "^0x[0-9a-fA-F]+$")) { v = v.Substring(2); if (byte.TryParse(v, NumberStyles.AllowHexSpecifier, formatter.NumberFormat, out var result)) { return result; } if (short.TryParse(v, NumberStyles.AllowHexSpecifier, formatter.NumberFormat, out var result2)) { return result2; } if (int.TryParse(v, NumberStyles.AllowHexSpecifier, formatter.NumberFormat, out var result3)) { return result3; } if (long.TryParse(v, NumberStyles.AllowHexSpecifier, formatter.NumberFormat, out var result4)) { return result4; } if (ulong.TryParse(v, NumberStyles.AllowHexSpecifier, formatter.NumberFormat, out var result5)) { return result5; } return v; } if (Regex.IsMatch(v, "^0o[0-9a-fA-F]+$")) { if (!TryAndSwallow(() => Convert.ToByte(v, 8), out object value2) && !TryAndSwallow(() => Convert.ToInt16(v, 8), out value2) && !TryAndSwallow(() => Convert.ToInt32(v, 8), out value2) && !TryAndSwallow(() => Convert.ToInt64(v, 8), out value2) && !TryAndSwallow(() => Convert.ToUInt64(v, 8), out value2)) { return v; } return value2; } if (Regex.IsMatch(v, "^[-+]?(\\.[0-9]+|[0-9]+(\\.[0-9]*)?)([eE][-+]?[0-9]+)?$")) { if (byte.TryParse(v, NumberStyles.Integer, formatter.NumberFormat, out var result6)) { return result6; } if (short.TryParse(v, NumberStyles.Integer, formatter.NumberFormat, out var result7)) { return result7; } if (int.TryParse(v, NumberStyles.Integer, formatter.NumberFormat, out var result8)) { return result8; } if (long.TryParse(v, NumberStyles.Integer, formatter.NumberFormat, out var result9)) { return result9; } if (ulong.TryParse(v, NumberStyles.Integer, formatter.NumberFormat, out var result10)) { return result10; } if (float.TryParse(v, NumberStyles.Float, formatter.NumberFormat, out var result11)) { return result11; } if (double.TryParse(v, NumberStyles.Float, formatter.NumberFormat, out var result12)) { return result12; } return v; } if (Regex.IsMatch(v, "^[-+]?(\\.inf|\\.Inf|\\.INF)$")) { if (Polyfills.StartsWith(v, '-')) { return float.NegativeInfinity; } return float.PositiveInfinity; } if (Regex.IsMatch(v, "^(\\.nan|\\.NaN|\\.NAN)$")) { return float.NaN; } return v; } } private static bool TryAndSwallow(Func attempt, out object? value) { try { value = attempt(); return true; } catch { value = null; return false; } } } internal sealed class StaticArrayNodeDeserializer : INodeDeserializer { private sealed class ArrayList : IList, ICollection, IEnumerable { private object?[] data; public bool IsFixedSize => false; public bool IsReadOnly => false; public object? this[int index] { get { return data[index]; } set { data[index] = value; } } public int Count { get; private set; } public bool IsSynchronized => false; public object SyncRoot => data; public ArrayList() { Clear(); } public int Add(object? value) { if (Count == data.Length) { Array.Resize(ref data, data.Length * 2); } data[Count] = value; return Count++; } public void Clear() { data = new object[10]; Count = 0; } bool IList.Contains(object? value) { throw new NotSupportedException(); } int IList.IndexOf(object? value) { throw new NotSupportedException(); } void IList.Insert(int index, object? value) { throw new NotSupportedException(); } void IList.Remove(object? value) { throw new NotSupportedException(); } void IList.RemoveAt(int index) { throw new NotSupportedException(); } public void CopyTo(Array array, int index) { Array.Copy(data, 0, array, index, Count); } public IEnumerator GetEnumerator() { int i = 0; while (i < Count) { yield return data[i]; int num = i + 1; i = num; } } } private readonly StaticObjectFactory factory; public StaticArrayNodeDeserializer(StaticObjectFactory factory) { this.factory = factory ?? throw new ArgumentNullException("factory"); } public bool Deserialize(IParser parser, Type expectedType, Func nestedObjectDeserializer, out object? value, ObjectDeserializer rootDeserializer) { if (!factory.IsArray(expectedType)) { value = false; return false; } Type valueType = factory.GetValueType(expectedType); ArrayList arrayList = new ArrayList(); StaticCollectionNodeDeserializer.DeserializeHelper(valueType, parser, nestedObjectDeserializer, arrayList, factory); Array array = factory.CreateArray(expectedType, arrayList.Count); arrayList.CopyTo(array, 0); value = array; return true; } } internal sealed class StaticCollectionNodeDeserializer : INodeDeserializer { private readonly StaticObjectFactory factory; public StaticCollectionNodeDeserializer(StaticObjectFactory factory) { this.factory = factory ?? throw new ArgumentNullException("factory"); } public bool Deserialize(IParser parser, Type expectedType, Func nestedObjectDeserializer, out object? value, ObjectDeserializer rootDeserializer) { if (!factory.IsList(expectedType)) { value = null; return false; } DeserializeHelper(result: (IList)(value = factory.Create(expectedType) as IList), tItem: factory.GetValueType(expectedType), parser: parser, nestedObjectDeserializer: nestedObjectDeserializer, factory: factory); return true; } internal static void DeserializeHelper(Type tItem, IParser parser, Func nestedObjectDeserializer, IList result, IObjectFactory factory) { parser.Consume(); SequenceEnd @event; while (!parser.TryConsume(out @event)) { ParsingEvent current = parser.Current; object obj = nestedObjectDeserializer(parser, tItem); if (obj is IValuePromise valuePromise) { int index = result.Add(factory.CreatePrimitive(tItem)); valuePromise.ValueAvailable += delegate(object? v) { result[index] = v; }; } else { result.Add(obj); } } } } internal class StaticDictionaryNodeDeserializer : DictionaryDeserializer, INodeDeserializer { private readonly StaticObjectFactory objectFactory; public StaticDictionaryNodeDeserializer(StaticObjectFactory objectFactory, bool duplicateKeyChecking) : base(duplicateKeyChecking) { this.objectFactory = objectFactory ?? throw new ArgumentNullException("objectFactory"); } public bool Deserialize(IParser reader, Type expectedType, Func nestedObjectDeserializer, out object? value, ObjectDeserializer rootDeserializer) { if (objectFactory.IsDictionary(expectedType)) { if (!(objectFactory.Create(expectedType) is IDictionary dictionary)) { value = null; return false; } Type keyType = objectFactory.GetKeyType(expectedType); Type valueType = objectFactory.GetValueType(expectedType); value = dictionary; base.Deserialize(keyType, valueType, reader, nestedObjectDeserializer, dictionary, rootDeserializer); return true; } value = null; return false; } } internal sealed class TypeConverterNodeDeserializer : INodeDeserializer { private readonly TypeConverterCache converters; public TypeConverterNodeDeserializer(IEnumerable converters) { this.converters = new TypeConverterCache(converters); } public bool Deserialize(IParser parser, Type expectedType, Func nestedObjectDeserializer, out object? value, ObjectDeserializer rootDeserializer) { if (!converters.TryGetConverterForType(expectedType, out IYamlTypeConverter typeConverter)) { value = null; return false; } value = typeConverter.ReadYaml(parser, expectedType, rootDeserializer); return true; } } internal sealed class YamlConvertibleNodeDeserializer : INodeDeserializer { private readonly IObjectFactory objectFactory; public YamlConvertibleNodeDeserializer(IObjectFactory objectFactory) { this.objectFactory = objectFactory; } public bool Deserialize(IParser parser, Type expectedType, Func nestedObjectDeserializer, out object? value, ObjectDeserializer rootDeserializer) { if (typeof(IYamlConvertible).IsAssignableFrom(expectedType)) { IYamlConvertible yamlConvertible = (IYamlConvertible)objectFactory.Create(expectedType); yamlConvertible.Read(parser, expectedType, (Type type) => nestedObjectDeserializer(parser, type)); value = yamlConvertible; return true; } value = null; return false; } } internal sealed class YamlSerializableNodeDeserializer : INodeDeserializer { private readonly IObjectFactory objectFactory; public YamlSerializableNodeDeserializer(IObjectFactory objectFactory) { this.objectFactory = objectFactory; } public bool Deserialize(IParser parser, Type expectedType, Func nestedObjectDeserializer, out object? value, ObjectDeserializer rootDeserializer) { if (typeof(IYamlSerializable).IsAssignableFrom(expectedType)) { IYamlSerializable yamlSerializable = (IYamlSerializable)objectFactory.Create(expectedType); yamlSerializable.ReadYaml(parser); value = yamlSerializable; return true; } value = null; return false; } } } namespace YamlDotNet.Serialization.NamingConventions { internal sealed class CamelCaseNamingConvention : INamingConvention { public static readonly INamingConvention Instance = new CamelCaseNamingConvention(); [Obsolete("Use the Instance static field instead of creating new instances")] public CamelCaseNamingConvention() { } public string Apply(string value) { return value.ToCamelCase(); } public string Reverse(string value) { return value.ToPascalCase(); } } internal sealed class HyphenatedNamingConvention : INamingConvention { public static readonly INamingConvention Instance = new HyphenatedNamingConvention(); [Obsolete("Use the Instance static field instead of creating new instances")] public HyphenatedNamingConvention() { } public string Apply(string value) { return value.FromCamelCase("-"); } public string Reverse(string value) { return value.ToPascalCase(); } } internal sealed class LowerCaseNamingConvention : INamingConvention { public static readonly INamingConvention Instance = new LowerCaseNamingConvention(); private LowerCaseNamingConvention() { } public string Apply(string value) { return value.ToCamelCase().ToLower(CultureInfo.InvariantCulture); } public string Reverse(string value) { if (string.IsNullOrEmpty(value)) { return value; } return char.ToUpperInvariant(value[0]) + value.Substring(1); } } internal sealed class NullNamingConvention : INamingConvention { public static readonly INamingConvention Instance = new NullNamingConvention(); [Obsolete("Use the Instance static field instead of creating new instances")] public NullNamingConvention() { } public string Apply(string value) { return value; } public string Reverse(string value) { return value; } } internal sealed class PascalCaseNamingConvention : INamingConvention { public static readonly INamingConvention Instance = new PascalCaseNamingConvention(); [Obsolete("Use the Instance static field instead of creating new instances")] public PascalCaseNamingConvention() { } public string Apply(string value) { return value.ToPascalCase(); } public string Reverse(string value) { return value.ToPascalCase(); } } internal sealed class UnderscoredNamingConvention : INamingConvention { public static readonly INamingConvention Instance = new UnderscoredNamingConvention(); [Obsolete("Use the Instance static field instead of creating new instances")] public UnderscoredNamingConvention() { } public string Apply(string value) { return value.FromCamelCase("_"); } public string Reverse(string value) { return value.ToPascalCase(); } } } namespace YamlDotNet.Serialization.EventEmitters { internal abstract class ChainedEventEmitter : IEventEmitter { protected readonly IEventEmitter nextEmitter; protected ChainedEventEmitter(IEventEmitter nextEmitter) { this.nextEmitter = nextEmitter ?? throw new ArgumentNullException("nextEmitter"); } public virtual void Emit(AliasEventInfo eventInfo, IEmitter emitter) { nextEmitter.Emit(eventInfo, emitter); } public virtual void Emit(ScalarEventInfo eventInfo, IEmitter emitter) { nextEmitter.Emit(eventInfo, emitter); } public virtual void Emit(MappingStartEventInfo eventInfo, IEmitter emitter) { nextEmitter.Emit(eventInfo, emitter); } public virtual void Emit(MappingEndEventInfo eventInfo, IEmitter emitter) { nextEmitter.Emit(eventInfo, emitter); } public virtual void Emit(SequenceStartEventInfo eventInfo, IEmitter emitter) { nextEmitter.Emit(eventInfo, emitter); } public virtual void Emit(SequenceEndEventInfo eventInfo, IEmitter emitter) { nextEmitter.Emit(eventInfo, emitter); } } internal sealed class JsonEventEmitter : ChainedEventEmitter { private readonly YamlFormatter formatter; private readonly INamingConvention enumNamingConvention; private readonly ITypeInspector typeInspector; public JsonEventEmitter(IEventEmitter nextEmitter, YamlFormatter formatter, INamingConvention enumNamingConvention, ITypeInspector typeInspector) : base(nextEmitter) { this.formatter = formatter; this.enumNamingConvention = enumNamingConvention; this.typeInspector = typeInspector; } public override void Emit(AliasEventInfo eventInfo, IEmitter emitter) { eventInfo.NeedsExpansion = true; } public override void Emit(ScalarEventInfo eventInfo, IEmitter emitter) { eventInfo.IsPlainImplicit = true; eventInfo.Style = ScalarStyle.Plain; object value = eventInfo.Source.Value; if (value == null) { eventInfo.RenderedValue = "null"; } else { TypeCode typeCode = eventInfo.Source.Type.GetTypeCode(); switch (typeCode) { case TypeCode.Boolean: eventInfo.RenderedValue = formatter.FormatBoolean(value); break; case TypeCode.SByte: case TypeCode.Byte: case TypeCode.Int16: case TypeCode.UInt16: case TypeCode.Int32: case TypeCode.UInt32: case TypeCode.Int64: case TypeCode.UInt64: if (eventInfo.Source.Type.IsEnum()) { eventInfo.RenderedValue = formatter.FormatEnum(value, typeInspector, enumNamingConvention); eventInfo.Style = ((!formatter.PotentiallyQuoteEnums(value)) ? ScalarStyle.Plain : ScalarStyle.DoubleQuoted); } else { eventInfo.RenderedValue = formatter.FormatNumber(value); } break; case TypeCode.Single: { float f = (float)value; eventInfo.RenderedValue = f.ToString("G", CultureInfo.InvariantCulture); if (float.IsNaN(f) || float.IsInfinity(f)) { eventInfo.Style = ScalarStyle.DoubleQuoted; } break; } case TypeCode.Double: { double d = (double)value; eventInfo.RenderedValue = d.ToString("G", CultureInfo.InvariantCulture); if (double.IsNaN(d) || double.IsInfinity(d)) { eventInfo.Style = ScalarStyle.DoubleQuoted; } break; } case TypeCode.Decimal: eventInfo.RenderedValue = ((decimal)value).ToString(CultureInfo.InvariantCulture); break; case TypeCode.Char: case TypeCode.String: eventInfo.RenderedValue = value.ToString(); eventInfo.Style = ScalarStyle.DoubleQuoted; break; case TypeCode.DateTime: eventInfo.RenderedValue = formatter.FormatDateTime(value); break; case TypeCode.Empty: eventInfo.RenderedValue = "null"; break; default: if (eventInfo.Source.Type == typeof(TimeSpan)) { eventInfo.RenderedValue = formatter.FormatTimeSpan(value); break; } throw new NotSupportedException($"TypeCode.{typeCode} is not supported."); } } base.Emit(eventInfo, emitter); } public override void Emit(MappingStartEventInfo eventInfo, IEmitter emitter) { eventInfo.Style = MappingStyle.Flow; base.Emit(eventInfo, emitter); } public override void Emit(SequenceStartEventInfo eventInfo, IEmitter emitter) { eventInfo.Style = SequenceStyle.Flow; base.Emit(eventInfo, emitter); } } internal sealed class TypeAssigningEventEmitter : ChainedEventEmitter { private readonly IDictionary tagMappings; private readonly bool quoteNecessaryStrings; private readonly Regex? isSpecialStringValue_Regex; private static readonly string SpecialStrings_Pattern = "^(null|Null|NULL|\\~|true|True|TRUE|false|False|FALSE|[-+]?[0-9]+|0o[0-7]+|0x[0-9a-fA-F]+|[-+]?(\\.[0-9]+|[0-9]+(\\.[0-9]*)?)([eE][-+]?[0-9]+)?|[-+]?(\\.inf|\\.Inf|\\.INF)|\\.nan|\\.NaN|\\.NAN|\\s.*)$"; private static readonly string CombinedYaml1_1SpecialStrings_Pattern = "^(null|Null|NULL|\\~|true|True|TRUE|false|False|FALSE|y|Y|yes|Yes|YES|n|N|no|No|NO|on|On|ON|off|Off|OFF|[-+]?0b[0-1_]+|[-+]?0o?[0-7_]+|[-+]?(0|[1-9][0-9_]*)|[-+]?0x[0-9a-fA-F_]+|[-+]?[1-9][0-9_]*(:[0-5]?[0-9])+|[-+]?([0-9][0-9_]*)?\\.[0-9_]*([eE][-+][0-9]+)?|[-+]?[0-9][0-9_]*(:[0-5]?[0-9])+\\.[0-9_]*|[-+]?\\.(inf|Inf|INF)|\\.(nan|NaN|NAN))$"; private readonly ScalarStyle defaultScalarStyle; private readonly YamlFormatter formatter; private readonly INamingConvention enumNamingConvention; private readonly ITypeInspector typeInspector; public TypeAssigningEventEmitter(IEventEmitter nextEmitter, IDictionary tagMappings, bool quoteNecessaryStrings, bool quoteYaml1_1Strings, ScalarStyle defaultScalarStyle, YamlFormatter formatter, INamingConvention enumNamingConvention, ITypeInspector typeInspector) : base(nextEmitter) { this.defaultScalarStyle = defaultScalarStyle; this.formatter = formatter; this.tagMappings = tagMappings; this.quoteNecessaryStrings = quoteNecessaryStrings; isSpecialStringValue_Regex = new Regex(quoteYaml1_1Strings ? CombinedYaml1_1SpecialStrings_Pattern : SpecialStrings_Pattern, RegexOptions.Compiled); this.enumNamingConvention = enumNamingConvention; this.typeInspector = typeInspector; } public override void Emit(ScalarEventInfo eventInfo, IEmitter emitter) { ScalarStyle style = ScalarStyle.Plain; object value = eventInfo.Source.Value; if (value == null) { eventInfo.Tag = JsonSchema.Tags.Null; eventInfo.RenderedValue = ""; } else { TypeCode typeCode = eventInfo.Source.Type.GetTypeCode(); switch (typeCode) { case TypeCode.Boolean: eventInfo.Tag = JsonSchema.Tags.Bool; eventInfo.RenderedValue = formatter.FormatBoolean(value); break; case TypeCode.SByte: case TypeCode.Byte: case TypeCode.Int16: case TypeCode.UInt16: case TypeCode.Int32: case TypeCode.UInt32: case TypeCode.Int64: case TypeCode.UInt64: if (eventInfo.Source.Type.IsEnum) { eventInfo.Tag = FailsafeSchema.Tags.Str; eventInfo.RenderedValue = formatter.FormatEnum(value, typeInspector, enumNamingConvention); style = ((!quoteNecessaryStrings || !IsSpecialStringValue(eventInfo.RenderedValue) || !formatter.PotentiallyQuoteEnums(value)) ? defaultScalarStyle : ScalarStyle.DoubleQuoted); } else { eventInfo.Tag = JsonSchema.Tags.Int; eventInfo.RenderedValue = formatter.FormatNumber(value); } break; case TypeCode.Single: eventInfo.Tag = JsonSchema.Tags.Float; eventInfo.RenderedValue = formatter.FormatNumber((float)value); break; case TypeCode.Double: eventInfo.Tag = JsonSchema.Tags.Float; eventInfo.RenderedValue = formatter.FormatNumber((double)value); break; case TypeCode.Decimal: eventInfo.Tag = JsonSchema.Tags.Float; eventInfo.RenderedValue = formatter.FormatNumber(value); break; case TypeCode.Char: case TypeCode.String: eventInfo.Tag = FailsafeSchema.Tags.Str; eventInfo.RenderedValue = value.ToString(); style = ((!quoteNecessaryStrings || !IsSpecialStringValue(eventInfo.RenderedValue)) ? defaultScalarStyle : ScalarStyle.DoubleQuoted); break; case TypeCode.DateTime: eventInfo.Tag = DefaultSchema.Tags.Timestamp; eventInfo.RenderedValue = formatter.FormatDateTime(value); break; case TypeCode.Empty: eventInfo.Tag = JsonSchema.Tags.Null; eventInfo.RenderedValue = ""; break; default: if (eventInfo.Source.Type == typeof(TimeSpan)) { eventInfo.RenderedValue = formatter.FormatTimeSpan(value); break; } throw new NotSupportedException($"TypeCode.{typeCode} is not supported."); } } eventInfo.IsPlainImplicit = true; if (eventInfo.Style == ScalarStyle.Any) { eventInfo.Style = style; } base.Emit(eventInfo, emitter); } public override void Emit(MappingStartEventInfo eventInfo, IEmitter emitter) { AssignTypeIfNeeded(eventInfo); base.Emit(eventInfo, emitter); } public override void Emit(SequenceStartEventInfo eventInfo, IEmitter emitter) { AssignTypeIfNeeded(eventInfo); base.Emit(eventInfo, emitter); } private void AssignTypeIfNeeded(ObjectEventInfo eventInfo) { if (tagMappings.TryGetValue(eventInfo.Source.Type, out var value)) { eventInfo.Tag = value; } } private bool IsSpecialStringValue(string value) { if (value.Trim() == string.Empty) { return true; } return isSpecialStringValue_Regex?.IsMatch(value) ?? false; } } internal sealed class WriterEventEmitter : IEventEmitter { void IEventEmitter.Emit(AliasEventInfo eventInfo, IEmitter emitter) { emitter.Emit(new YamlDotNet.Core.Events.AnchorAlias(eventInfo.Alias)); } void IEventEmitter.Emit(ScalarEventInfo eventInfo, IEmitter emitter) { emitter.Emit(new YamlDotNet.Core.Events.Scalar(eventInfo.Anchor, eventInfo.Tag, eventInfo.RenderedValue, eventInfo.Style, eventInfo.IsPlainImplicit, eventInfo.IsQuotedImplicit)); } void IEventEmitter.Emit(MappingStartEventInfo eventInfo, IEmitter emitter) { emitter.Emit(new MappingStart(eventInfo.Anchor, eventInfo.Tag, eventInfo.IsImplicit, eventInfo.Style)); } void IEventEmitter.Emit(MappingEndEventInfo eventInfo, IEmitter emitter) { emitter.Emit(new MappingEnd()); } void IEventEmitter.Emit(SequenceStartEventInfo eventInfo, IEmitter emitter) { emitter.Emit(new SequenceStart(eventInfo.Anchor, eventInfo.Tag, eventInfo.IsImplicit, eventInfo.Style)); } void IEventEmitter.Emit(SequenceEndEventInfo eventInfo, IEmitter emitter) { emitter.Emit(new SequenceEnd()); } } } namespace YamlDotNet.Serialization.Converters { internal class DateTime8601Converter : IYamlTypeConverter { private readonly ScalarStyle scalarStyle; public DateTime8601Converter() : this(ScalarStyle.Any) { } public DateTime8601Converter(ScalarStyle scalarStyle) { this.scalarStyle = scalarStyle; } public bool Accepts(Type type) { return type == typeof(DateTime); } public object ReadYaml(IParser parser, Type type, ObjectDeserializer rootDeserializer) { string value = parser.Consume().Value; DateTime dateTime = DateTime.ParseExact(value, "O", CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind); return dateTime; } public void WriteYaml(IEmitter emitter, object? value, Type type, ObjectSerializer serializer) { string value2 = ((DateTime)value).ToString("O", CultureInfo.InvariantCulture); emitter.Emit(new YamlDotNet.Core.Events.Scalar(AnchorName.Empty, TagName.Empty, value2, scalarStyle, isPlainImplicit: true, isQuotedImplicit: false)); } } internal class DateTimeConverter : IYamlTypeConverter { private readonly DateTimeKind kind; private readonly IFormatProvider provider; private readonly bool doubleQuotes; private readonly string[] formats; public DateTimeConverter(DateTimeKind kind = DateTimeKind.Utc, IFormatProvider? provider = null, bool doubleQuotes = false, params string[] formats) { this.kind = ((kind == DateTimeKind.Unspecified) ? DateTimeKind.Utc : kind); this.provider = provider ?? CultureInfo.InvariantCulture; this.doubleQuotes = doubleQuotes; this.formats = formats.DefaultIfEmpty("G").ToArray(); } public bool Accepts(Type type) { return type == typeof(DateTime); } public object ReadYaml(IParser parser, Type type, ObjectDeserializer rootDeserializer) { string value = parser.Consume().Value; DateTimeStyles style = ((kind == DateTimeKind.Local) ? DateTimeStyles.AssumeLocal : DateTimeStyles.AssumeUniversal); DateTime dt = DateTime.ParseExact(value, formats, provider, style); dt = EnsureDateTimeKind(dt, kind); return dt; } public void WriteYaml(IEmitter emitter, object? value, Type type, ObjectSerializer serializer) { DateTime dateTime = (DateTime)value; string value2 = ((kind == DateTimeKind.Local) ? dateTime.ToLocalTime() : dateTime.ToUniversalTime()).ToString(formats.First(), provider); emitter.Emit(new YamlDotNet.Core.Events.Scalar(AnchorName.Empty, TagName.Empty, value2, doubleQuotes ? ScalarStyle.DoubleQuoted : ScalarStyle.Any, isPlainImplicit: true, isQuotedImplicit: false)); } private static DateTime EnsureDateTimeKind(DateTime dt, DateTimeKind kind) { if (dt.Kind == DateTimeKind.Local && kind == DateTimeKind.Utc) { return dt.ToUniversalTime(); } if (dt.Kind == DateTimeKind.Utc && kind == DateTimeKind.Local) { return dt.ToLocalTime(); } return dt; } } internal class DateTimeOffsetConverter : IYamlTypeConverter { private readonly IFormatProvider provider; private readonly ScalarStyle style; private readonly DateTimeStyles dateStyle; private readonly string[] formats; public DateTimeOffsetConverter(IFormatProvider? provider = null, ScalarStyle style = ScalarStyle.Any, DateTimeStyles dateStyle = DateTimeStyles.None, params string[] formats) { this.provider = provider ?? CultureInfo.InvariantCulture; this.style = style; this.dateStyle = dateStyle; this.formats = formats.DefaultIfEmpty("O").ToArray(); } public bool Accepts(Type type) { return type == typeof(DateTimeOffset); } public object ReadYaml(IParser parser, Type type, ObjectDeserializer rootDeserializer) { string value = parser.Consume().Value; DateTimeOffset dateTimeOffset = DateTimeOffset.ParseExact(value, formats, provider, dateStyle); return dateTimeOffset; } public void WriteYaml(IEmitter emitter, object? value, Type type, ObjectSerializer serializer) { string value2 = ((DateTimeOffset)value).ToString(formats.First(), provider); emitter.Emit(new YamlDotNet.Core.Events.Scalar(AnchorName.Empty, TagName.Empty, value2, style, isPlainImplicit: true, isQuotedImplicit: false)); } } internal class GuidConverter : IYamlTypeConverter { private readonly bool jsonCompatible; public GuidConverter(bool jsonCompatible) { this.jsonCompatible = jsonCompatible; } public bool Accepts(Type type) { return type == typeof(Guid); } public object ReadYaml(IParser parser, Type type, ObjectDeserializer rootDeserializer) { string value = parser.Consume().Value; return new Guid(value); } public void WriteYaml(IEmitter emitter, object? value, Type type, ObjectSerializer serializer) { Guid guid = (Guid)value; emitter.Emit(new YamlDotNet.Core.Events.Scalar(AnchorName.Empty, TagName.Empty, guid.ToString("D"), jsonCompatible ? ScalarStyle.DoubleQuoted : ScalarStyle.Any, isPlainImplicit: true, isQuotedImplicit: false)); } } internal class SystemTypeConverter : IYamlTypeConverter { public bool Accepts(Type type) { return typeof(Type).IsAssignableFrom(type); } public object ReadYaml(IParser parser, Type type, ObjectDeserializer rootDeserializer) { string value = parser.Consume().Value; return Type.GetType(value, throwOnError: true); } public void WriteYaml(IEmitter emitter, object? value, Type type, ObjectSerializer serializer) { Type type2 = (Type)value; emitter.Emit(new YamlDotNet.Core.Events.Scalar(AnchorName.Empty, TagName.Empty, type2.AssemblyQualifiedName, ScalarStyle.Any, isPlainImplicit: true, isQuotedImplicit: false)); } } } namespace YamlDotNet.Serialization.Callbacks { [AttributeUsage(AttributeTargets.Method)] internal sealed class OnDeserializedAttribute : Attribute { } [AttributeUsage(AttributeTargets.Method)] internal sealed class OnDeserializingAttribute : Attribute { } [AttributeUsage(AttributeTargets.Method)] internal sealed class OnSerializedAttribute : Attribute { } [AttributeUsage(AttributeTargets.Method)] internal sealed class OnSerializingAttribute : Attribute { } } namespace YamlDotNet.Serialization.BufferedDeserialization { internal interface ITypeDiscriminatingNodeDeserializerOptions { void AddTypeDiscriminator(ITypeDiscriminator discriminator); void AddKeyValueTypeDiscriminator(string discriminatorKey, IDictionary valueTypeMapping); void AddUniqueKeyTypeDiscriminator(IDictionary uniqueKeyTypeMapping); } internal class ParserBuffer : IParser { private readonly LinkedList buffer; private LinkedListNode? current; public ParsingEvent? Current => current?.Value; public ParserBuffer(IParser parserToBuffer, int maxDepth, int maxLength) { buffer = new LinkedList(); buffer.AddLast(parserToBuffer.Consume()); int num = 0; do { ParsingEvent parsingEvent = parserToBuffer.Consume(); num += parsingEvent.NestingIncrease; buffer.AddLast(parsingEvent); if (maxDepth > -1 && num > maxDepth) { throw new ArgumentOutOfRangeException("parserToBuffer", "Parser buffer exceeded max depth"); } if (maxLength > -1 && buffer.Count > maxLength) { throw new ArgumentOutOfRangeException("parserToBuffer", "Parser buffer exceeded max length"); } } while (num >= 0); current = buffer.First; } public bool MoveNext() { current = current?.Next; return current != null; } public void Reset() { current = buffer.First; } } internal class TypeDiscriminatingNodeDeserializer : INodeDeserializer { private readonly IList innerDeserializers; private readonly IList typeDiscriminators; private readonly int maxDepthToBuffer; private readonly int maxLengthToBuffer; public TypeDiscriminatingNodeDeserializer(IList innerDeserializers, IList typeDiscriminators, int maxDepthToBuffer, int maxLengthToBuffer) { this.innerDeserializers = innerDeserializers; this.typeDiscriminators = typeDiscriminators; this.maxDepthToBuffer = maxDepthToBuffer; this.maxLengthToBuffer = maxLengthToBuffer; } public bool Deserialize(IParser reader, Type expectedType, Func nestedObjectDeserializer, out object? value, ObjectDeserializer rootDeserializer) { if (!reader.Accept(out var _)) { value = null; return false; } IEnumerable enumerable = typeDiscriminators.Where((ITypeDiscriminator t) => t.BaseType.IsAssignableFrom(expectedType)); if (!enumerable.Any()) { value = null; return false; } Mark start = reader.Current.Start; Type expectedType2 = expectedType; ParserBuffer parserBuffer; try { parserBuffer = new ParserBuffer(reader, maxDepthToBuffer, maxLengthToBuffer); } catch (Exception innerException) { throw new YamlException(in start, reader.Current.End, "Failed to buffer yaml node", innerException); } try { foreach (ITypeDiscriminator item in enumerable) { parserBuffer.Reset(); if (item.TryDiscriminate(parserBuffer, out Type suggestedType)) { expectedType2 = suggestedType; break; } } } catch (Exception innerException2) { throw new YamlException(in start, reader.Current.End, "Failed to discriminate type", innerException2); } parserBuffer.Reset(); foreach (INodeDeserializer innerDeserializer in innerDeserializers) { if (innerDeserializer.Deserialize(parserBuffer, expectedType2, nestedObjectDeserializer, out value, rootDeserializer)) { return true; } } value = null; return false; } } internal class TypeDiscriminatingNodeDeserializerOptions : ITypeDiscriminatingNodeDeserializerOptions { internal readonly List discriminators = new List(); public void AddTypeDiscriminator(ITypeDiscriminator discriminator) { discriminators.Add(discriminator); } public void AddKeyValueTypeDiscriminator(string discriminatorKey, IDictionary valueTypeMapping) { discriminators.Add(new KeyValueTypeDiscriminator(typeof(T), discriminatorKey, valueTypeMapping)); } public void AddUniqueKeyTypeDiscriminator(IDictionary uniqueKeyTypeMapping) { discriminators.Add(new UniqueKeyTypeDiscriminator(typeof(T), uniqueKeyTypeMapping)); } } } namespace YamlDotNet.Serialization.BufferedDeserialization.TypeDiscriminators { internal interface ITypeDiscriminator { Type BaseType { get; } bool TryDiscriminate(IParser buffer, out Type? suggestedType); } internal class KeyValueTypeDiscriminator : ITypeDiscriminator { private readonly string targetKey; private readonly IDictionary typeMapping; public Type BaseType { get; private set; } public KeyValueTypeDiscriminator(Type baseType, string targetKey, IDictionary typeMapping) { foreach (KeyValuePair item in typeMapping) { if (!baseType.IsAssignableFrom(item.Value)) { throw new ArgumentOutOfRangeException("typeMapping", $"{item.Value} is not a assignable to {baseType}"); } } BaseType = baseType; this.targetKey = targetKey; this.typeMapping = typeMapping; } public bool TryDiscriminate(IParser parser, out Type? suggestedType) { if (parser.TryFindMappingEntry((YamlDotNet.Core.Events.Scalar scalar2) => targetKey == scalar2.Value, out YamlDotNet.Core.Events.Scalar _, out ParsingEvent value) && value is YamlDotNet.Core.Events.Scalar scalar && typeMapping.TryGetValue(scalar.Value, out Type value2)) { suggestedType = value2; return true; } suggestedType = null; return false; } } internal class UniqueKeyTypeDiscriminator : ITypeDiscriminator { private readonly IDictionary typeMapping; public Type BaseType { get; private set; } public UniqueKeyTypeDiscriminator(Type baseType, IDictionary typeMapping) { foreach (KeyValuePair item in typeMapping) { if (!baseType.IsAssignableFrom(item.Value)) { throw new ArgumentOutOfRangeException("typeMapping", $"{item.Value} is not a assignable to {baseType}"); } } BaseType = baseType; this.typeMapping = typeMapping; } public bool TryDiscriminate(IParser parser, out Type? suggestedType) { if (parser.TryFindMappingEntry((YamlDotNet.Core.Events.Scalar scalar) => typeMapping.ContainsKey(scalar.Value), out YamlDotNet.Core.Events.Scalar key, out ParsingEvent _)) { suggestedType = typeMapping[key.Value]; return true; } suggestedType = null; return false; } } } namespace YamlDotNet.RepresentationModel { internal class DocumentLoadingState { private readonly Dictionary anchors = new Dictionary(); private readonly List nodesWithUnresolvedAliases = new List(); public void AddAnchor(YamlNode node) { if (node.Anchor.IsEmpty) { throw new ArgumentException("The specified node does not have an anchor"); } anchors[node.Anchor] = node; } public YamlNode GetNode(AnchorName anchor, Mark start, Mark end) { if (anchors.TryGetValue(anchor, out YamlNode value)) { return value; } throw new AnchorNotFoundException(in start, in end, $"The anchor '{anchor}' does not exists"); } public bool TryGetNode(AnchorName anchor, [NotNullWhen(true)] out YamlNode? node) { return anchors.TryGetValue(anchor, out node); } public void AddNodeWithUnresolvedAliases(YamlNode node) { nodesWithUnresolvedAliases.Add(node); } public void ResolveAliases() { foreach (YamlNode nodesWithUnresolvedAlias in nodesWithUnresolvedAliases) { nodesWithUnresolvedAlias.ResolveAliases(this); } } } internal class EmitterState { public HashSet EmittedAnchors { get; } = new HashSet(); } internal interface IYamlVisitor { void Visit(YamlStream stream); void Visit(YamlDocument document); void Visit(YamlScalarNode scalar); void Visit(YamlSequenceNode sequence); void Visit(YamlMappingNode mapping); } internal class LibYamlEventStream { private readonly IParser parser; public LibYamlEventStream(IParser iParser) { parser = iParser ?? throw new ArgumentNullException("iParser"); } public void WriteTo(TextWriter textWriter) { while (parser.MoveNext()) { ParsingEvent current = parser.Current; if (!(current is YamlDotNet.Core.Events.AnchorAlias anchorAlias)) { if (!(current is YamlDotNet.Core.Events.DocumentEnd documentEnd)) { if (!(current is YamlDotNet.Core.Events.DocumentStart documentStart)) { if (!(current is MappingEnd)) { if (!(current is MappingStart nodeEvent)) { if (!(current is YamlDotNet.Core.Events.Scalar scalar)) { if (!(current is SequenceEnd)) { if (!(current is SequenceStart nodeEvent2)) { if (!(current is YamlDotNet.Core.Events.StreamEnd)) { if (current is YamlDotNet.Core.Events.StreamStart) { textWriter.Write("+STR"); } } else { textWriter.Write("-STR"); } } else { textWriter.Write("+SEQ"); WriteAnchorAndTag(textWriter, nodeEvent2); } } else { textWriter.Write("-SEQ"); } } else { textWriter.Write("=VAL"); WriteAnchorAndTag(textWriter, scalar); switch (scalar.Style) { case ScalarStyle.DoubleQuoted: textWriter.Write(" \""); break; case ScalarStyle.SingleQuoted: textWriter.Write(" '"); break; case ScalarStyle.Folded: textWriter.Write(" >"); break; case ScalarStyle.Literal: textWriter.Write(" |"); break; default: textWriter.Write(" :"); break; } string value = scalar.Value; foreach (char c in value) { switch (c) { case '\b': textWriter.Write("\\b"); break; case '\t': textWriter.Write("\\t"); break; case '\n': textWriter.Write("\\n"); break; case '\r': textWriter.Write("\\r"); break; case '\\': textWriter.Write("\\\\"); break; default: textWriter.Write(c); break; } } } } else { textWriter.Write("+MAP"); WriteAnchorAndTag(textWriter, nodeEvent); } } else { textWriter.Write("-MAP"); } } else { textWriter.Write("+DOC"); if (!documentStart.IsImplicit) { textWriter.Write(" ---"); } } } else { textWriter.Write("-DOC"); if (!documentEnd.IsImplicit) { textWriter.Write(" ..."); } } } else { textWriter.Write("=ALI *"); textWriter.Write(anchorAlias.Value); } textWriter.WriteLine(); } } private static void WriteAnchorAndTag(TextWriter textWriter, NodeEvent nodeEvent) { if (!nodeEvent.Anchor.IsEmpty) { textWriter.Write(" &"); textWriter.Write(nodeEvent.Anchor); } if (!nodeEvent.Tag.IsEmpty) { textWriter.Write(" <"); textWriter.Write(nodeEvent.Tag.Value); textWriter.Write(">"); } } } internal class YamlAliasNode : YamlNode { public override YamlNodeType NodeType => YamlNodeType.Alias; internal YamlAliasNode(AnchorName anchor) { base.Anchor = anchor; } internal override void ResolveAliases(DocumentLoadingState state) { throw new NotSupportedException("Resolving an alias on an alias node does not make sense"); } internal override void Emit(IEmitter emitter, EmitterState state) { throw new NotSupportedException("A YamlAliasNode is an implementation detail and should never be saved."); } public override void Accept(IYamlVisitor visitor) { throw new NotSupportedException("A YamlAliasNode is an implementation detail and should never be visited."); } public override bool Equals(object? obj) { if (obj is YamlAliasNode yamlAliasNode && Equals(yamlAliasNode)) { return object.Equals(base.Anchor, yamlAliasNode.Anchor); } return false; } public override int GetHashCode() { return base.GetHashCode(); } internal override string ToString(RecursionLevel level) { return "*" + base.Anchor; } internal override IEnumerable SafeAllNodes(RecursionLevel level) { yield return this; } } internal class YamlDocument { private class AnchorAssigningVisitor : YamlVisitorBase { private readonly HashSet existingAnchors = new HashSet(); private readonly Dictionary visitedNodes = new Dictionary(); public void AssignAnchors(YamlDocument document) { existingAnchors.Clear(); visitedNodes.Clear(); document.Accept(this); Random random = new Random(); foreach (KeyValuePair visitedNode in visitedNodes) { if (!visitedNode.Value) { continue; } AnchorName anchorName; if (!visitedNode.Key.Anchor.IsEmpty && !existingAnchors.Contains(visitedNode.Key.Anchor)) { anchorName = visitedNode.Key.Anchor; } else { do { anchorName = new AnchorName(random.Next().ToString(CultureInfo.InvariantCulture)); } while (existingAnchors.Contains(anchorName)); } existingAnchors.Add(anchorName); visitedNode.Key.Anchor = anchorName; } } private bool VisitNodeAndFindDuplicates(YamlNode node) { if (visitedNodes.TryGetValue(node, out var value)) { if (!value) { visitedNodes[node] = true; } return !value; } visitedNodes.Add(node, value: false); return false; } public override void Visit(YamlScalarNode scalar) { VisitNodeAndFindDuplicates(scalar); } public override void Visit(YamlMappingNode mapping) { if (!VisitNodeAndFindDuplicates(mapping)) { base.Visit(mapping); } } public override void Visit(YamlSequenceNode sequence) { if (!VisitNodeAndFindDuplicates(sequence)) { base.Visit(sequence); } } } public YamlNode RootNode { get; private set; } public IEnumerable AllNodes => RootNode.AllNodes; public YamlDocument(YamlNode rootNode) { RootNode = rootNode; } public YamlDocument(string rootNode) { RootNode = new YamlScalarNode(rootNode); } internal YamlDocument(IParser parser) { DocumentLoadingState documentLoadingState = new DocumentLoadingState(); parser.Consume(); YamlDotNet.Core.Events.DocumentEnd @event; while (!parser.TryConsume(out @event)) { RootNode = YamlNode.ParseNode(parser, documentLoadingState); if (RootNode is YamlAliasNode) { throw new YamlException("A document cannot contain only an alias"); } } documentLoadingState.ResolveAliases(); if (RootNode == null) { throw new ArgumentException("Atempted to parse an empty document"); } } private void AssignAnchors() { AnchorAssigningVisitor anchorAssigningVisitor = new AnchorAssigningVisitor(); anchorAssigningVisitor.AssignAnchors(this); } internal void Save(IEmitter emitter, bool assignAnchors = true) { if (assignAnchors) { AssignAnchors(); } emitter.Emit(new YamlDotNet.Core.Events.DocumentStart()); RootNode.Save(emitter, new EmitterState()); emitter.Emit(new YamlDotNet.Core.Events.DocumentEnd(isImplicit: false)); } public void Accept(IYamlVisitor visitor) { visitor.Visit(this); } } internal sealed class YamlMappingNode : YamlNode, IEnumerable>, IEnumerable, IYamlConvertible { private readonly OrderedDictionary children = new OrderedDictionary(); public IOrderedDictionary Children => children; public MappingStyle Style { get; set; } public override YamlNodeType NodeType => YamlNodeType.Mapping; internal YamlMappingNode(IParser parser, DocumentLoadingState state) { Load(parser, state); } private void Load(IParser parser, DocumentLoadingState state) { MappingStart mappingStart = parser.Consume(); Load(mappingStart, state); Style = mappingStart.Style; bool flag = false; MappingEnd @event; while (!parser.TryConsume(out @event)) { YamlNode yamlNode = YamlNode.ParseNode(parser, state); YamlNode yamlNode2 = YamlNode.ParseNode(parser, state); if (!children.TryAdd(yamlNode, yamlNode2)) { throw new YamlException(yamlNode.Start, yamlNode.End, $"Duplicate key {yamlNode}"); } flag = flag || yamlNode is YamlAliasNode || yamlNode2 is YamlAliasNode; } if (flag) { state.AddNodeWithUnresolvedAliases(this); } } public YamlMappingNode() { } public YamlMappingNode(params KeyValuePair[] children) : this((IEnumerable>)children) { } public YamlMappingNode(IEnumerable> children) { foreach (KeyValuePair child in children) { this.children.Add(child); } } public YamlMappingNode(params YamlNode[] children) : this((IEnumerable)children) { } public YamlMappingNode(IEnumerable children) { using IEnumerator enumerator = children.GetEnumerator(); while (enumerator.MoveNext()) { YamlNode current = enumerator.Current; if (!enumerator.MoveNext()) { throw new ArgumentException("When constructing a mapping node with a sequence, the number of elements of the sequence must be even."); } Add(current, enumerator.Current); } } public void Add(YamlNode key, YamlNode value) { children.Add(key, value); } public void Add(string key, YamlNode value) { children.Add(new YamlScalarNode(key), value); } public void Add(YamlNode key, string value) { children.Add(key, new YamlScalarNode(value)); } public void Add(string key, string value) { children.Add(new YamlScalarNode(key), new YamlScalarNode(value)); } internal override void ResolveAliases(DocumentLoadingState state) { Dictionary dictionary = null; Dictionary dictionary2 = null; foreach (KeyValuePair child in children) { if (child.Key is YamlAliasNode) { if (dictionary == null) { dictionary = new Dictionary(); } dictionary.Add(child.Key, state.GetNode(child.Key.Anchor, child.Key.Start, child.Key.End)); } if (child.Value is YamlAliasNode) { if (dictionary2 == null) { dictionary2 = new Dictionary(); } dictionary2.Add(child.Key, state.GetNode(child.Value.Anchor, child.Value.Start, child.Value.End)); } } if (dictionary2 != null) { foreach (KeyValuePair item in dictionary2) { children[item.Key] = item.Value; } } if (dictionary == null) { return; } foreach (KeyValuePair item2 in dictionary) { YamlNode value = children[item2.Key]; children.Remove(item2.Key); children.Add(item2.Value, value); } } internal override void Emit(IEmitter emitter, EmitterState state) { emitter.Emit(new MappingStart(base.Anchor, base.Tag, isImplicit: true, Style)); foreach (KeyValuePair child in children) { child.Key.Save(emitter, state); child.Value.Save(emitter, state); } emitter.Emit(new MappingEnd()); } public override void Accept(IYamlVisitor visitor) { visitor.Visit(this); } public override bool Equals(object? obj) { if (!(obj is YamlMappingNode yamlMappingNode) || !object.Equals(base.Tag, yamlMappingNode.Tag) || children.Count != yamlMappingNode.children.Count) { return false; } foreach (KeyValuePair child in children) { if (!yamlMappingNode.children.TryGetValue(child.Key, out YamlNode value) || !object.Equals(child.Value, value)) { return false; } } return true; } public override int GetHashCode() { int num = base.GetHashCode(); foreach (KeyValuePair child in children) { num = YamlDotNet.Core.HashCode.CombineHashCodes(num, child.Key); num = (child.Value.Anchor.IsEmpty ? YamlDotNet.Core.HashCode.CombineHashCodes(num, child.Value) : YamlDotNet.Core.HashCode.CombineHashCodes(num, child.Value.Anchor)); } return num; } internal override IEnumerable SafeAllNodes(RecursionLevel level) { level.Increment(); yield return this; foreach (KeyValuePair child in children) { foreach (YamlNode item in child.Key.SafeAllNodes(level)) { yield return item; } foreach (YamlNode item2 in child.Value.SafeAllNodes(level)) { yield return item2; } } level.Decrement(); } internal override string ToString(RecursionLevel level) { if (!level.TryIncrement()) { return "WARNING! INFINITE RECURSION!"; } StringBuilderPool.BuilderWrapper builderWrapper = StringBuilderPool.Rent(); try { StringBuilder builder = builderWrapper.Builder; builder.Append("{ "); foreach (KeyValuePair child in children) { if (builder.Length > 2) { builder.Append(", "); } builder.Append("{ ").Append(child.Key.ToString(level)).Append(", ") .Append(child.Value.ToString(level)) .Append(" }"); } builder.Append(" }"); level.Decrement(); return builder.ToString(); } finally { ((IDisposable)builderWrapper/*cast due to .constrained prefix*/).Dispose(); } } public IEnumerator> GetEnumerator() { return children.GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } void IYamlConvertible.Read(IParser parser, Type expectedType, ObjectDeserializer nestedObjectDeserializer) { Load(parser, new DocumentLoadingState()); } void IYamlConvertible.Write(IEmitter emitter, ObjectSerializer nestedObjectSerializer) { Emit(emitter, new EmitterState()); } public static YamlMappingNode FromObject(object mapping) { if (mapping == null) { throw new ArgumentNullException("mapping"); } YamlMappingNode yamlMappingNode = new YamlMappingNode(); foreach (PropertyInfo publicProperty in mapping.GetType().GetPublicProperties()) { if (publicProperty.CanRead && publicProperty.GetGetMethod(nonPublic: false).GetParameters().Length == 0) { object value = publicProperty.GetValue(mapping, null); YamlNode yamlNode = value as YamlNode; if (yamlNode == null) { string text = Convert.ToString(value, CultureInfo.InvariantCulture); yamlNode = text ?? string.Empty; } yamlMappingNode.Add(publicProperty.Name, yamlNode); } } return yamlMappingNode; } } internal abstract class YamlNode { private const int MaximumRecursionLevel = 1000; internal const string MaximumRecursionLevelReachedToStringValue = "WARNING! INFINITE RECURSION!"; public AnchorName Anchor { get; set; } public TagName Tag { get; set; } public Mark Start { get; private set; } = Mark.Empty; public Mark End { get; private set; } = Mark.Empty; public IEnumerable AllNodes { get { RecursionLevel level = new RecursionLevel(1000); return SafeAllNodes(level); } } public abstract YamlNodeType NodeType { get; } public YamlNode this[int index] { get { if (!(this is YamlSequenceNode yamlSequenceNode)) { throw new ArgumentException($"Accessed '{NodeType}' with an invalid index: {index}. Only Sequences can be indexed by number."); } return yamlSequenceNode.Children[index]; } } public YamlNode this[YamlNode key] { get { if (!(this is YamlMappingNode yamlMappingNode)) { throw new ArgumentException($"Accessed '{NodeType}' with an invalid index: {key}. Only Mappings can be indexed by key."); } return yamlMappingNode.Children[key]; } } internal void Load(NodeEvent yamlEvent, DocumentLoadingState state) { Tag = yamlEvent.Tag; if (!yamlEvent.Anchor.IsEmpty) { Anchor = yamlEvent.Anchor; state.AddAnchor(this); } Start = yamlEvent.Start; End = yamlEvent.End; } internal static YamlNode ParseNode(IParser parser, DocumentLoadingState state) { if (parser.Accept(out var _)) { return new YamlScalarNode(parser, state); } if (parser.Accept(out var _)) { return new YamlSequenceNode(parser, state); } if (parser.Accept(out var _)) { return new YamlMappingNode(parser, state); } if (parser.TryConsume(out var event4)) { if (!state.TryGetNode(event4.Value, out YamlNode node)) { return new YamlAliasNode(event4.Value); } return node; } throw new ArgumentException("The current event is of an unsupported type.", "parser"); } internal abstract void ResolveAliases(DocumentLoadingState state); internal void Save(IEmitter emitter, EmitterState state) { if (!Anchor.IsEmpty && !state.EmittedAnchors.Add(Anchor)) { emitter.Emit(new YamlDotNet.Core.Events.AnchorAlias(Anchor)); } else { Emit(emitter, state); } } internal abstract void Emit(IEmitter emitter, EmitterState state); public abstract void Accept(IYamlVisitor visitor); public override string ToString() { RecursionLevel recursionLevel = new RecursionLevel(1000); return ToString(recursionLevel); } internal abstract string ToString(RecursionLevel level); internal abstract IEnumerable SafeAllNodes(RecursionLevel level); public static implicit operator YamlNode(string value) { return new YamlScalarNode(value); } public static implicit operator YamlNode(string[] sequence) { return new YamlSequenceNode(((IEnumerable)sequence).Select((Func)((string i) => i))); } public static explicit operator string?(YamlNode node) { if (!(node is YamlScalarNode yamlScalarNode)) { throw new ArgumentException($"Attempted to convert a '{node.NodeType}' to string. This conversion is valid only for Scalars."); } return yamlScalarNode.Value; } } internal sealed class YamlNodeIdentityEqualityComparer : IEqualityComparer { public bool Equals([AllowNull] YamlNode x, [AllowNull] YamlNode y) { return x == y; } public int GetHashCode(YamlNode obj) { return obj.GetHashCode(); } } internal enum YamlNodeType { Alias, Mapping, Scalar, Sequence } [DebuggerDisplay("{Value}")] internal sealed class YamlScalarNode : YamlNode, IYamlConvertible { private bool forceImplicitPlain; private string? value; public string? Value { get { return value; } set { if (value == null) { forceImplicitPlain = true; } else { forceImplicitPlain = false; } this.value = value; } } public ScalarStyle Style { get; set; } public override YamlNodeType NodeType => YamlNodeType.Scalar; internal YamlScalarNode(IParser parser, DocumentLoadingState state) { Load(parser, state); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private void Load(IParser parser, DocumentLoadingState state) { YamlDotNet.Core.Events.Scalar scalar = parser.Consume(); Load(scalar, state); string text = scalar.Value; if (scalar.Style == ScalarStyle.Plain && base.Tag.IsEmpty) { forceImplicitPlain = text.Length switch { 0 => true, 1 => text == "~", 4 => text == "null" || text == "Null" || text == "NULL", _ => false, }; } value = text; Style = scalar.Style; } public YamlScalarNode() { } public YamlScalarNode(string? value) { Value = value; } internal override void ResolveAliases(DocumentLoadingState state) { throw new NotSupportedException("Resolving an alias on a scalar node does not make sense"); } internal override void Emit(IEmitter emitter, EmitterState state) { TagName tag = base.Tag; bool isPlainImplicit = tag.IsEmpty; if (forceImplicitPlain && Style == ScalarStyle.Plain && (Value == null || Value == "")) { tag = JsonSchema.Tags.Null; isPlainImplicit = true; } else if (tag.IsEmpty && Value == null && (Style == ScalarStyle.Plain || Style == ScalarStyle.Any)) { tag = JsonSchema.Tags.Null; isPlainImplicit = true; } emitter.Emit(new YamlDotNet.Core.Events.Scalar(base.Anchor, tag, Value ?? string.Empty, Style, isPlainImplicit, isQuotedImplicit: false)); } public override void Accept(IYamlVisitor visitor) { visitor.Visit(this); } public override bool Equals(object? obj) { if (obj is YamlScalarNode yamlScalarNode && object.Equals(base.Tag, yamlScalarNode.Tag)) { return object.Equals(Value, yamlScalarNode.Value); } return false; } public override int GetHashCode() { return YamlDotNet.Core.HashCode.CombineHashCodes(base.Tag.GetHashCode(), Value); } public static explicit operator string?(YamlScalarNode value) { return value.Value; } internal override string ToString(RecursionLevel level) { return Value ?? string.Empty; } internal override IEnumerable SafeAllNodes(RecursionLevel level) { yield return this; } void IYamlConvertible.Read(IParser parser, Type expectedType, ObjectDeserializer nestedObjectDeserializer) { Load(parser, new DocumentLoadingState()); } void IYamlConvertible.Write(IEmitter emitter, ObjectSerializer nestedObjectSerializer) { Emit(emitter, new EmitterState()); } } [DebuggerDisplay("Count = {children.Count}")] internal sealed class YamlSequenceNode : YamlNode, IEnumerable, IEnumerable, IYamlConvertible { private readonly List children = new List(); public IList Children => children; public SequenceStyle Style { get; set; } public override YamlNodeType NodeType => YamlNodeType.Sequence; internal YamlSequenceNode(IParser parser, DocumentLoadingState state) { Load(parser, state); } private void Load(IParser parser, DocumentLoadingState state) { SequenceStart sequenceStart = parser.Consume(); Load(sequenceStart, state); Style = sequenceStart.Style; bool flag = false; SequenceEnd @event; while (!parser.TryConsume(out @event)) { YamlNode yamlNode = YamlNode.ParseNode(parser, state); children.Add(yamlNode); flag = flag || yamlNode is YamlAliasNode; } if (flag) { state.AddNodeWithUnresolvedAliases(this); } } public YamlSequenceNode() { } public YamlSequenceNode(params YamlNode[] children) : this((IEnumerable)children) { } public YamlSequenceNode(IEnumerable children) { foreach (YamlNode child in children) { this.children.Add(child); } } public void Add(YamlNode child) { children.Add(child); } public void Add(string child) { children.Add(new YamlScalarNode(child)); } internal override void ResolveAliases(DocumentLoadingState state) { for (int i = 0; i < children.Count; i++) { if (children[i] is YamlAliasNode) { children[i] = state.GetNode(children[i].Anchor, children[i].Start, children[i].End); } } } internal override void Emit(IEmitter emitter, EmitterState state) { emitter.Emit(new SequenceStart(base.Anchor, base.Tag, base.Tag.IsEmpty, Style)); foreach (YamlNode child in children) { child.Save(emitter, state); } emitter.Emit(new SequenceEnd()); } public override void Accept(IYamlVisitor visitor) { visitor.Visit(this); } public override bool Equals(object? obj) { if (!(obj is YamlSequenceNode yamlSequenceNode) || !object.Equals(base.Tag, yamlSequenceNode.Tag) || children.Count != yamlSequenceNode.children.Count) { return false; } for (int i = 0; i < children.Count; i++) { if (!object.Equals(children[i], yamlSequenceNode.children[i])) { return false; } } return true; } public override int GetHashCode() { int h = 0; foreach (YamlNode child in children) { h = YamlDotNet.Core.HashCode.CombineHashCodes(h, child); } return YamlDotNet.Core.HashCode.CombineHashCodes(h, base.Tag); } internal override IEnumerable SafeAllNodes(RecursionLevel level) { level.Increment(); yield return this; foreach (YamlNode child in children) { foreach (YamlNode item in child.SafeAllNodes(level)) { yield return item; } } level.Decrement(); } internal override string ToString(RecursionLevel level) { if (!level.TryIncrement()) { return "WARNING! INFINITE RECURSION!"; } StringBuilderPool.BuilderWrapper builderWrapper = StringBuilderPool.Rent(); try { StringBuilder builder = builderWrapper.Builder; builder.Append("[ "); foreach (YamlNode child in children) { if (builder.Length > 2) { builder.Append(", "); } builder.Append(child.ToString(level)); } builder.Append(" ]"); level.Decrement(); return builder.ToString(); } finally { ((IDisposable)builderWrapper/*cast due to .constrained prefix*/).Dispose(); } } public IEnumerator GetEnumerator() { return Children.GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } void IYamlConvertible.Read(IParser parser, Type expectedType, ObjectDeserializer nestedObjectDeserializer) { Load(parser, new DocumentLoadingState()); } void IYamlConvertible.Write(IEmitter emitter, ObjectSerializer nestedObjectSerializer) { Emit(emitter, new EmitterState()); } } internal class YamlStream : IEnumerable, IEnumerable { private readonly List documents = new List(); public IList Documents => documents; public YamlStream() { } public YamlStream(params YamlDocument[] documents) : this((IEnumerable)documents) { } public YamlStream(IEnumerable documents) { foreach (YamlDocument document in documents) { this.documents.Add(document); } } public void Add(YamlDocument document) { documents.Add(document); } public void Load(TextReader input) { Load(new Parser(input)); } public void Load(IParser parser) { documents.Clear(); parser.Consume(); YamlDotNet.Core.Events.StreamEnd @event; while (!parser.TryConsume(out @event)) { YamlDocument item = new YamlDocument(parser); documents.Add(item); } } public void Save(TextWriter output) { Save(output, assignAnchors: true); } public void Save(TextWriter output, bool assignAnchors) { Save(new Emitter(output), assignAnchors); } public void Save(IEmitter emitter, bool assignAnchors) { emitter.Emit(new YamlDotNet.Core.Events.StreamStart()); foreach (YamlDocument document in documents) { document.Save(emitter, assignAnchors); } emitter.Emit(new YamlDotNet.Core.Events.StreamEnd()); } public void Accept(IYamlVisitor visitor) { visitor.Visit(this); } public IEnumerator GetEnumerator() { return documents.GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } } [Obsolete("Use YamlVisitorBase")] internal abstract class YamlVisitor : IYamlVisitor { protected virtual void Visit(YamlStream stream) { } protected virtual void Visited(YamlStream stream) { } protected virtual void Visit(YamlDocument document) { } protected virtual void Visited(YamlDocument document) { } protected virtual void Visit(YamlScalarNode scalar) { } protected virtual void Visited(YamlScalarNode scalar) { } protected virtual void Visit(YamlSequenceNode sequence) { } protected virtual void Visited(YamlSequenceNode sequence) { } protected virtual void Visit(YamlMappingNode mapping) { } protected virtual void Visited(YamlMappingNode mapping) { } protected virtual void VisitChildren(YamlStream stream) { foreach (YamlDocument document in stream.Documents) { document.Accept(this); } } protected virtual void VisitChildren(YamlDocument document) { if (document.RootNode != null) { document.RootNode.Accept(this); } } protected virtual void VisitChildren(YamlSequenceNode sequence) { foreach (YamlNode child in sequence.Children) { child.Accept(this); } } protected virtual void VisitChildren(YamlMappingNode mapping) { foreach (KeyValuePair child in mapping.Children) { child.Key.Accept(this); child.Value.Accept(this); } } void IYamlVisitor.Visit(YamlStream stream) { Visit(stream); VisitChildren(stream); Visited(stream); } void IYamlVisitor.Visit(YamlDocument document) { Visit(document); VisitChildren(document); Visited(document); } void IYamlVisitor.Visit(YamlScalarNode scalar) { Visit(scalar); Visited(scalar); } void IYamlVisitor.Visit(YamlSequenceNode sequence) { Visit(sequence); VisitChildren(sequence); Visited(sequence); } void IYamlVisitor.Visit(YamlMappingNode mapping) { Visit(mapping); VisitChildren(mapping); Visited(mapping); } } internal abstract class YamlVisitorBase : IYamlVisitor { public virtual void Visit(YamlStream stream) { VisitChildren(stream); } public virtual void Visit(YamlDocument document) { VisitChildren(document); } public virtual void Visit(YamlScalarNode scalar) { } public virtual void Visit(YamlSequenceNode sequence) { VisitChildren(sequence); } public virtual void Visit(YamlMappingNode mapping) { VisitChildren(mapping); } protected virtual void VisitPair(YamlNode key, YamlNode value) { key.Accept(this); value.Accept(this); } protected virtual void VisitChildren(YamlStream stream) { foreach (YamlDocument document in stream.Documents) { document.Accept(this); } } protected virtual void VisitChildren(YamlDocument document) { if (document.RootNode != null) { document.RootNode.Accept(this); } } protected virtual void VisitChildren(YamlSequenceNode sequence) { foreach (YamlNode child in sequence.Children) { child.Accept(this); } } protected virtual void VisitChildren(YamlMappingNode mapping) { foreach (KeyValuePair child in mapping.Children) { VisitPair(child.Key, child.Value); } } } } namespace YamlDotNet.Helpers { internal class DefaultFsharpHelper : IFsharpHelper { private static bool IsFsharpCore(Type t) { return t.Namespace == "Microsoft.FSharp.Core"; } public bool IsOptionType(Type t) { if (IsFsharpCore(t)) { return t.Name == "FSharpOption`1"; } return false; } public Type? GetOptionUnderlyingType(Type t) { if (!t.IsGenericType || !IsOptionType(t)) { return null; } return t.GenericTypeArguments[0]; } public object? GetValue(IObjectDescriptor objectDescriptor) { if (!IsOptionType(objectDescriptor.Type)) { throw new InvalidOperationException("Should not be called on non-Option<> type"); } if (objectDescriptor.Value == null) { return null; } return objectDescriptor.Type.GetProperty("Value").GetValue(objectDescriptor.Value); } public bool IsFsharpListType(Type t) { if (t.Namespace == "Microsoft.FSharp.Collections") { return t.Name == "FSharpList`1"; } return false; } public object? CreateFsharpListFromArray(Type t, Type itemsType, Array arr) { if (!IsFsharpListType(t)) { return null; } return t.Assembly.GetType("Microsoft.FSharp.Collections.ListModule").GetMethod("OfArray").MakeGenericMethod(itemsType) .Invoke(null, new object[1] { arr }); } } internal static class DictionaryExtensions { public static bool TryAdd(this Dictionary dictionary, T key, V value) { if (dictionary.ContainsKey(key)) { return false; } dictionary.Add(key, value); return true; } public static TValue GetOrAdd(this ConcurrentDictionary dictionary, TKey key, Func valueFactory, TArg arg) { if (dictionary == null) { throw new ArgumentNullException("dictionary"); } if (key == null) { throw new ArgumentNullException("key"); } if (valueFactory == null) { throw new ArgumentNullException("valueFactory"); } TValue value; do { if (dictionary.TryGetValue(key, out value)) { return value; } value = valueFactory(key, arg); } while (!dictionary.TryAdd(key, value)); return value; } } internal static class ExpressionExtensions { public static PropertyInfo AsProperty(this LambdaExpression propertyAccessor) { PropertyInfo propertyInfo = TryGetMemberExpression(propertyAccessor); if (propertyInfo == null) { throw new ArgumentException("Expected a lambda expression in the form: x => x.SomeProperty", "propertyAccessor"); } return propertyInfo; } [return: MaybeNull] private static TMemberInfo TryGetMemberExpression(LambdaExpression lambdaExpression) where TMemberInfo : MemberInfo { if (lambdaExpression.Parameters.Count != 1) { return null; } Expression expression = lambdaExpression.Body; if (expression is UnaryExpression unaryExpression) { if (unaryExpression.NodeType != ExpressionType.Convert) { return null; } expression = unaryExpression.Operand; } if (expression is MemberExpression memberExpression) { if (memberExpression.Expression != lambdaExpression.Parameters[0]) { return null; } return memberExpression.Member as TMemberInfo; } return null; } } internal static class FsharpHelper { public static IFsharpHelper? Instance { get; set; } public static bool IsOptionType(Type t) { return Instance?.IsOptionType(t) ?? false; } public static Type? GetOptionUnderlyingType(Type t) { return Instance?.GetOptionUnderlyingType(t); } public static object? GetValue(IObjectDescriptor objectDescriptor) { return Instance?.GetValue(objectDescriptor); } public static bool IsFsharpListType(Type t) { return Instance?.IsFsharpListType(t) ?? false; } public static object? CreateFsharpListFromArray(Type t, Type itemsType, Array arr) { return Instance?.CreateFsharpListFromArray(t, itemsType, arr); } } internal sealed class GenericCollectionToNonGenericAdapter : IList, ICollection, IEnumerable { private readonly ICollection genericCollection; public bool IsFixedSize { get { throw new NotSupportedException(); } } public bool IsReadOnly { get { throw new NotSupportedException(); } } public object? this[int index] { get { throw new NotSupportedException(); } set { ((IList)genericCollection)[index] = (T)value; } } public int Count { get { throw new NotSupportedException(); } } public bool IsSynchronized { get { throw new NotSupportedException(); } } public object SyncRoot { get { throw new NotSupportedException(); } } public GenericCollectionToNonGenericAdapter(ICollection genericCollection) { this.genericCollection = genericCollection ?? throw new ArgumentNullException("genericCollection"); } public int Add(object? value) { int count = genericCollection.Count; genericCollection.Add((T)value); return count; } public void Clear() { genericCollection.Clear(); } public bool Contains(object? value) { throw new NotSupportedException(); } public int IndexOf(object? value) { throw new NotSupportedException(); } public void Insert(int index, object? value) { throw new NotSupportedException(); } public void Remove(object? value) { throw new NotSupportedException(); } public void RemoveAt(int index) { throw new NotSupportedException(); } public void CopyTo(Array array, int index) { throw new NotSupportedException(); } public IEnumerator GetEnumerator() { return genericCollection.GetEnumerator(); } } internal sealed class GenericDictionaryToNonGenericAdapter : IDictionary, ICollection, IEnumerable where TKey : notnull { private class DictionaryEnumerator : IDictionaryEnumerator, IEnumerator { private readonly IEnumerator> enumerator; public DictionaryEntry Entry => new DictionaryEntry(Key, Value); public object Key => enumerator.Current.Key; public object? Value => enumerator.Current.Value; public object Current => Entry; public DictionaryEnumerator(IEnumerator> enumerator) { this.enumerator = enumerator; } public bool MoveNext() { return enumerator.MoveNext(); } public void Reset() { enumerator.Reset(); } } private readonly IDictionary genericDictionary; public bool IsFixedSize { get { throw new NotSupportedException(); } } public bool IsReadOnly { get { throw new NotSupportedException(); } } public ICollection Keys { get { throw new NotSupportedException(); } } public ICollection Values { get { throw new NotSupportedException(); } } public object? this[object key] { get { throw new NotSupportedException(); } set { genericDictionary[(TKey)key] = (TValue)value; } } public int Count { get { throw new NotSupportedException(); } } public bool IsSynchronized { get { throw new NotSupportedException(); } } public object SyncRoot { get { throw new NotSupportedException(); } } public GenericDictionaryToNonGenericAdapter(IDictionary genericDictionary) { this.genericDictionary = genericDictionary ?? throw new ArgumentNullException("genericDictionary"); } public void Add(object key, object? value) { throw new NotSupportedException(); } public void Clear() { throw new NotSupportedException(); } public bool Contains(object key) { throw new NotSupportedException(); } public IDictionaryEnumerator GetEnumerator() { return new DictionaryEnumerator(genericDictionary.GetEnumerator()); } public void Remove(object key) { throw new NotSupportedException(); } public void CopyTo(Array array, int index) { throw new NotSupportedException(); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } } internal interface IFsharpHelper { bool IsOptionType(Type t); Type? GetOptionUnderlyingType(Type t); object? GetValue(IObjectDescriptor objectDescriptor); bool IsFsharpListType(Type t); object? CreateFsharpListFromArray(Type t, Type itemsType, Array arr); } internal interface IOrderedDictionary : IDictionary, ICollection>, IEnumerable>, IEnumerable where TKey : notnull { KeyValuePair this[int index] { get; set; } void Insert(int index, TKey key, TValue value); void RemoveAt(int index); } internal class NullFsharpHelper : IFsharpHelper { public object? CreateFsharpListFromArray(Type t, Type itemsType, Array arr) { return null; } public Type? GetOptionUnderlyingType(Type t) { return null; } public object? GetValue(IObjectDescriptor objectDescriptor) { return null; } public bool IsFsharpListType(Type t) { return false; } public bool IsOptionType(Type t) { return false; } } internal static class NumberExtensions { public static bool IsPowerOfTwo(this int value) { return (value & (value - 1)) == 0; } } [Serializable] internal sealed class OrderedDictionary : IOrderedDictionary, IDictionary, ICollection>, IEnumerable>, IEnumerable where TKey : notnull { private class KeyCollection : ICollection, IEnumerable, IEnumerable { private readonly OrderedDictionary orderedDictionary; public int Count => orderedDictionary.list.Count; public bool IsReadOnly => true; public void Add(TKey item) { throw new NotSupportedException(); } public void Clear() { throw new NotSupportedException(); } public bool Contains(TKey item) { return orderedDictionary.dictionary.ContainsKey(item); } public KeyCollection(OrderedDictionary orderedDictionary) { this.orderedDictionary = orderedDictionary; } public void CopyTo(TKey[] array, int arrayIndex) { for (int i = 0; i < orderedDictionary.list.Count; i++) { array[i] = orderedDictionary.list[i + arrayIndex].Key; } } public IEnumerator GetEnumerator() { return orderedDictionary.list.Select((KeyValuePair kvp) => kvp.Key).GetEnumerator(); } public bool Remove(TKey item) { throw new NotSupportedException(); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } } private class ValueCollection : ICollection, IEnumerable, IEnumerable { private readonly OrderedDictionary orderedDictionary; public int Count => orderedDictionary.list.Count; public bool IsReadOnly => true; public void Add(TValue item) { throw new NotSupportedException(); } public void Clear() { throw new NotSupportedException(); } public bool Contains(TValue item) { return orderedDictionary.dictionary.ContainsValue(item); } public ValueCollection(OrderedDictionary orderedDictionary) { this.orderedDictionary = orderedDictionary; } public void CopyTo(TValue[] array, int arrayIndex) { for (int i = 0; i < orderedDictionary.list.Count; i++) { array[i] = orderedDictionary.list[i + arrayIndex].Value; } } public IEnumerator GetEnumerator() { return orderedDictionary.list.Select((KeyValuePair kvp) => kvp.Value).GetEnumerator(); } public bool Remove(TValue item) { throw new NotSupportedException(); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } } [NonSerialized] private Dictionary dictionary; private readonly List> list; private readonly IEqualityComparer comparer; public TValue this[TKey key] { get { return dictionary[key]; } set { if (dictionary.ContainsKey(key)) { int index = list.FindIndex((KeyValuePair kvp) => comparer.Equals(kvp.Key, key)); dictionary[key] = value; list[index] = new KeyValuePair(key, value); } else { Add(key, value); } } } public ICollection Keys => new KeyCollection(this); public ICollection Values => new ValueCollection(this); public int Count => dictionary.Count; public bool IsReadOnly => false; public KeyValuePair this[int index] { get { return list[index]; } set { list[index] = value; } } public OrderedDictionary() : this((IEqualityComparer)EqualityComparer.Default) { } public OrderedDictionary(IEqualityComparer comparer) { list = new List>(); dictionary = new Dictionary(comparer); this.comparer = comparer; } public void Add(KeyValuePair item) { if (!TryAdd(item)) { ThrowDuplicateKeyException(item.Key); } } public void Add(TKey key, TValue value) { if (!TryAdd(key, value)) { ThrowDuplicateKeyException(key); } } private static void ThrowDuplicateKeyException(TKey key) { throw new ArgumentException($"An item with the same key {key} has already been added."); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool TryAdd(TKey key, TValue value) { if (DictionaryExtensions.TryAdd(dictionary, key, value)) { list.Add(new KeyValuePair(key, value)); return true; } return false; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool TryAdd(KeyValuePair item) { if (DictionaryExtensions.TryAdd(dictionary, item.Key, item.Value)) { list.Add(item); return true; } return false; } public void Clear() { dictionary.Clear(); list.Clear(); } public bool Contains(KeyValuePair item) { return dictionary.Contains(item); } public bool ContainsKey(TKey key) { return dictionary.ContainsKey(key); } public void CopyTo(KeyValuePair[] array, int arrayIndex) { list.CopyTo(array, arrayIndex); } public IEnumerator> GetEnumerator() { return list.GetEnumerator(); } public void Insert(int index, TKey key, TValue value) { dictionary.Add(key, value); list.Insert(index, new KeyValuePair(key, value)); } public bool Remove(TKey key) { if (dictionary.ContainsKey(key)) { int index = list.FindIndex((KeyValuePair kvp) => comparer.Equals(kvp.Key, key)); list.RemoveAt(index); if (!dictionary.Remove(key)) { throw new InvalidOperationException(); } return true; } return false; } public bool Remove(KeyValuePair item) { return Remove(item.Key); } public void RemoveAt(int index) { TKey key = list[index].Key; dictionary.Remove(key); list.RemoveAt(index); } public bool TryGetValue(TKey key, [MaybeNullWhen(false)] out TValue value) { return dictionary.TryGetValue(key, out value); } IEnumerator IEnumerable.GetEnumerator() { return list.GetEnumerator(); } [System.Runtime.Serialization.OnDeserialized] internal void OnDeserializedMethod(StreamingContext context) { dictionary = new Dictionary(); foreach (KeyValuePair item in list) { dictionary[item.Key] = item.Value; } } } internal static class ReadOnlyCollectionExtensions { public static IReadOnlyList AsReadonlyList(this List list) { return list; } public static IReadOnlyDictionary AsReadonlyDictionary(this Dictionary dictionary) where TKey : notnull { return dictionary; } } internal static class ThrowHelper { [MethodImpl(MethodImplOptions.NoInlining)] public static void ThrowArgumentOutOfRangeException(string paramName, string message) { throw new ArgumentOutOfRangeException(paramName, message); } } } namespace YamlDotNet.Core { internal readonly struct AnchorName : IEquatable { public static readonly AnchorName Empty; private static readonly Regex AnchorPattern = new Regex("^[^\\[\\]\\{\\},]+$", RegexOptions.Compiled); private readonly string? value; public string Value => value ?? throw new InvalidOperationException("Cannot read the Value of an empty anchor"); public bool IsEmpty => value == null; public AnchorName(string value) { this.value = value ?? throw new ArgumentNullException("value"); if (!AnchorPattern.IsMatch(value)) { throw new ArgumentException("Anchor cannot be empty or contain disallowed characters: []{},\nThe value was '" + value + "'.", "value"); } } public override string ToString() { return value ?? "[empty]"; } public bool Equals(AnchorName other) { return object.Equals(value, other.value); } public override bool Equals(object? obj) { if (obj is AnchorName other) { return Equals(other); } return false; } public override int GetHashCode() { return value?.GetHashCode() ?? 0; } public static bool operator ==(AnchorName left, AnchorName right) { return left.Equals(right); } public static bool operator !=(AnchorName left, AnchorName right) { return !(left == right); } public static implicit operator AnchorName(string? value) { if (value != null) { return new AnchorName(value); } return Empty; } } internal class AnchorNotFoundException : YamlException { public AnchorNotFoundException(string message) : base(message) { } public AnchorNotFoundException(in Mark start, in Mark end, string message) : base(in start, in end, message) { } public AnchorNotFoundException(string message, Exception inner) : base(message, inner) { } } [DebuggerStepThrough] internal readonly struct CharacterAnalyzer where TBuffer : ILookAheadBuffer { public TBuffer Buffer { get; } public bool EndOfInput => Buffer.EndOfInput; public CharacterAnalyzer(TBuffer buffer) { if (buffer == null) { throw new ArgumentNullException("buffer"); } Buffer = buffer; } public char Peek(int offset) { return Buffer.Peek(offset); } public void Skip(int length) { Buffer.Skip(length); } public bool IsAlphaNumericDashOrUnderscore(int offset = 0) { char c = Buffer.Peek(offset); if ((c < '0' || c > '9') && (c < 'A' || c > 'Z') && (c < 'a' || c > 'z') && c != '_') { return c == '-'; } return true; } public bool IsAscii(int offset = 0) { return Buffer.Peek(offset) <= '\u007f'; } public bool IsPrintable(int offset = 0) { char c = Buffer.Peek(offset); switch (c) { default: if (c != '\u0085' && (c < '\u00a0' || c > '\ud7ff')) { if (c >= '\ue000') { return c <= '\ufffd'; } return false; } break; case '\t': case '\n': case '\r': case ' ': case '!': case '"': case '#': case '$': case '%': case '&': case '\'': case '(': case ')': case '*': case '+': case ',': case '-': case '.': case '/': case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': case ':': case ';': case '<': case '=': case '>': case '?': case '@': case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G': case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U': case 'V': case 'W': case 'X': case 'Y': case 'Z': case '[': case '\\': case ']': case '^': case '_': case '`': case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g': case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n': case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u': case 'v': case 'w': case 'x': case 'y': case 'z': case '{': case '|': case '}': case '~': break; } return true; } public bool IsDigit(int offset = 0) { char c = Buffer.Peek(offset); if (c >= '0') { return c <= '9'; } return false; } public int AsDigit(int offset = 0) { return Buffer.Peek(offset) - 48; } public bool IsHex(int offset) { char c = Buffer.Peek(offset); if ((c < '0' || c > '9') && (c < 'A' || c > 'F')) { if (c >= 'a') { return c <= 'f'; } return false; } return true; } public int AsHex(int offset) { char c = Buffer.Peek(offset); if (c <= '9') { return c - 48; } if (c <= 'F') { return c - 65 + 10; } return c - 97 + 10; } public bool IsSpace(int offset = 0) { return Check(' ', offset); } public bool IsZero(int offset = 0) { return Check('\0', offset); } public bool IsTab(int offset = 0) { return Check('\t', offset); } public bool IsWhite(int offset = 0) { if (!IsSpace(offset)) { return IsTab(offset); } return true; } public bool IsBreak(int offset = 0) { return Check("\r\n\u0085\u2028\u2029", offset); } public bool IsCrLf(int offset = 0) { if (Check('\r', offset)) { return Check('\n', offset + 1); } return false; } public bool IsBreakOrZero(int offset = 0) { if (!IsBreak(offset)) { return IsZero(offset); } return true; } public bool IsWhiteBreakOrZero(int offset = 0) { if (!IsWhite(offset)) { return IsBreakOrZero(offset); } return true; } public bool Check(char expected, int offset = 0) { return Buffer.Peek(offset) == expected; } public bool Check(string expectedCharacters, int offset = 0) { char c = Buffer.Peek(offset); return Polyfills.Contains(expectedCharacters, c); } } internal static class Constants { public static readonly TagDirective[] DefaultTagDirectives = new TagDirective[2] { new TagDirective("!", "!"), new TagDirective("!!", "tag:yaml.org,2002:") }; public const int MajorVersion = 1; public const int MinorVersion = 3; } [DebuggerStepThrough] internal sealed class Cursor { public long Index { get; private set; } public long Line { get; private set; } public long LineOffset { get; private set; } public Cursor() { Line = 1L; } public Cursor(Cursor cursor) { Index = cursor.Index; Line = cursor.Line; LineOffset = cursor.LineOffset; } public Mark Mark() { return new Mark(Index, Line, LineOffset + 1); } public void Skip() { Index++; LineOffset++; } public void SkipLineByOffset(int offset) { Index += offset; Line++; LineOffset = 0L; } public void ForceSkipLineAfterNonBreak() { if (LineOffset != 0L) { Line++; LineOffset = 0L; } } } internal class Emitter : IEmitter { private class AnchorData { public AnchorName Anchor; public bool IsAlias; } private class TagData { public string? Handle; public string? Suffix; } private class ScalarData { public string Value = string.Empty; public bool IsMultiline; public bool IsFlowPlainAllowed; public bool IsBlockPlainAllowed; public bool IsSingleQuotedAllowed; public bool IsBlockAllowed; public bool HasSingleQuotes; public ScalarStyle Style; } private static readonly Regex UriReplacer = new Regex("[^0-9A-Za-z_\\-;?@=$~\\\\\\)\\]/:&+,\\.\\*\\(\\[!]", RegexOptions.Compiled | RegexOptions.Singleline); private static readonly string[] NewLineSeparators = new string[3] { "\r\n", "\r", "\n" }; private readonly TextWriter output; private readonly bool outputUsesUnicodeEncoding; private readonly int maxSimpleKeyLength; private readonly bool isCanonical; private readonly bool skipAnchorName; private readonly int bestIndent; private readonly int bestWidth; private EmitterState state; private readonly Stack states = new Stack(); private readonly Queue events = new Queue(); private readonly Stack indents = new Stack(); private readonly TagDirectiveCollection tagDirectives = new TagDirectiveCollection(); private int indent; private int flowLevel; private bool isMappingContext; private bool isSimpleKeyContext; private int column; private bool isWhitespace; private bool isIndentation; private readonly bool forceIndentLess; private readonly bool useUtf16SurrogatePair; private bool isDocumentEndWritten; private readonly AnchorData anchorData = new AnchorData(); private readonly TagData tagData = new TagData(); private readonly ScalarData scalarData = new ScalarData(); public Emitter(TextWriter output) : this(output, EmitterSettings.Default) { } public Emitter(TextWriter output, int bestIndent) : this(output, bestIndent, int.MaxValue) { } public Emitter(TextWriter output, int bestIndent, int bestWidth) : this(output, bestIndent, bestWidth, isCanonical: false) { } public Emitter(TextWriter output, int bestIndent, int bestWidth, bool isCanonical) : this(output, new EmitterSettings(bestIndent, bestWidth, isCanonical, 1024)) { } public Emitter(TextWriter output, EmitterSettings settings) { bestIndent = settings.BestIndent; bestWidth = settings.BestWidth; isCanonical = settings.IsCanonical; maxSimpleKeyLength = settings.MaxSimpleKeyLength; skipAnchorName = settings.SkipAnchorName; forceIndentLess = !settings.IndentSequences; useUtf16SurrogatePair = settings.UseUtf16SurrogatePairs; this.output = output; this.output.NewLine = settings.NewLine; outputUsesUnicodeEncoding = IsUnicode(output.Encoding); } public void Emit(ParsingEvent @event) { events.Enqueue(@event); while (!NeedMoreEvents()) { ParsingEvent evt = events.Peek(); try { AnalyzeEvent(evt); StateMachine(evt); } finally { events.Dequeue(); } } } private bool NeedMoreEvents() { if (events.Count == 0) { return true; } int num; switch (events.Peek().Type) { case EventType.DocumentStart: num = 1; break; case EventType.SequenceStart: num = 2; break; case EventType.MappingStart: num = 3; break; default: return false; } if (events.Count > num) { return false; } int num2 = 0; foreach (ParsingEvent @event in events) { switch (@event.Type) { case EventType.DocumentStart: case EventType.SequenceStart: case EventType.MappingStart: num2++; break; case EventType.DocumentEnd: case EventType.SequenceEnd: case EventType.MappingEnd: num2--; break; } if (num2 == 0) { return false; } } return true; } private void AnalyzeEvent(ParsingEvent evt) { anchorData.Anchor = AnchorName.Empty; tagData.Handle = null; tagData.Suffix = null; if (evt is YamlDotNet.Core.Events.AnchorAlias anchorAlias) { AnalyzeAnchor(anchorAlias.Value, isAlias: true); } else if (evt is NodeEvent nodeEvent) { if (evt is YamlDotNet.Core.Events.Scalar scalar) { AnalyzeScalar(scalar); } AnalyzeAnchor(nodeEvent.Anchor, isAlias: false); if (!nodeEvent.Tag.IsEmpty && (isCanonical || nodeEvent.IsCanonical)) { AnalyzeTag(nodeEvent.Tag); } } } private void AnalyzeAnchor(AnchorName anchor, bool isAlias) { anchorData.Anchor = anchor; anchorData.IsAlias = isAlias; } private void AnalyzeScalar(YamlDotNet.Core.Events.Scalar scalar) { string value = scalar.Value; scalarData.Value = value; if (value.Length == 0) { if (scalar.Tag == "tag:yaml.org,2002:null") { scalarData.IsMultiline = false; scalarData.IsFlowPlainAllowed = false; scalarData.IsBlockPlainAllowed = true; scalarData.IsSingleQuotedAllowed = false; scalarData.IsBlockAllowed = false; } else { scalarData.IsMultiline = false; scalarData.IsFlowPlainAllowed = false; scalarData.IsBlockPlainAllowed = false; scalarData.IsSingleQuotedAllowed = true; scalarData.IsBlockAllowed = false; } return; } bool flag = false; bool flag2 = false; if (value.StartsWith("---", StringComparison.Ordinal) || value.StartsWith("...", StringComparison.Ordinal)) { flag = true; flag2 = true; } StringLookAheadBufferPool.BufferWrapper bufferWrapper = StringLookAheadBufferPool.Rent(value); try { CharacterAnalyzer characterAnalyzer = new CharacterAnalyzer(bufferWrapper.Buffer); bool flag3 = true; bool flag4 = characterAnalyzer.IsWhiteBreakOrZero(1); bool flag5 = false; bool flag6 = false; bool flag7 = false; bool flag8 = false; bool flag9 = false; bool flag10 = false; bool flag11 = false; bool flag12 = false; bool flag13 = false; bool flag14 = false; bool flag15 = false; bool flag16 = !ValueIsRepresentableInOutputEncoding(value); bool flag17 = false; bool flag18 = false; bool flag19 = true; while (!characterAnalyzer.EndOfInput) { if (flag19) { if (characterAnalyzer.Check("#,[]{}&*!|>\"%@`'")) { flag = true; flag2 = true; flag9 = characterAnalyzer.Check('\''); flag17 |= characterAnalyzer.Check('\''); } if (characterAnalyzer.Check("?:")) { flag = true; if (flag4) { flag2 = true; } } if (characterAnalyzer.Check('-') && flag4) { flag = true; flag2 = true; } } else { if (characterAnalyzer.Check(",?[]{}")) { flag = true; } if (characterAnalyzer.Check(':')) { flag = true; if (flag4) { flag2 = true; } } if (characterAnalyzer.Check('#') && flag3) { flag = true; flag2 = true; } flag17 |= characterAnalyzer.Check('\''); } if (!flag16 && !characterAnalyzer.IsPrintable()) { flag16 = true; } if (characterAnalyzer.IsBreak()) { flag15 = true; } if (characterAnalyzer.IsSpace()) { if (flag19) { flag5 = true; } if (characterAnalyzer.Buffer.Position >= characterAnalyzer.Buffer.Length - 1) { flag7 = true; } if (flag13) { flag10 = true; flag14 = true; } flag12 = true; flag13 = false; } else if (characterAnalyzer.IsBreak()) { if (flag19) { flag6 = true; } if (characterAnalyzer.Buffer.Position >= characterAnalyzer.Buffer.Length - 1) { flag8 = true; } if (flag12) { flag11 = true; } if (flag14) { flag18 = true; } flag12 = false; flag13 = true; } else { flag12 = false; flag13 = false; flag14 = false; } flag3 = characterAnalyzer.IsWhiteBreakOrZero(); characterAnalyzer.Skip(1); if (!characterAnalyzer.EndOfInput) { flag4 = characterAnalyzer.IsWhiteBreakOrZero(1); } flag19 = false; } scalarData.IsFlowPlainAllowed = true; scalarData.IsBlockPlainAllowed = true; scalarData.IsSingleQuotedAllowed = true; scalarData.IsBlockAllowed = true; if (flag5 || flag6 || flag7 || flag8 || flag9) { scalarData.IsFlowPlainAllowed = false; scalarData.IsBlockPlainAllowed = false; } if (flag7) { scalarData.IsBlockAllowed = false; } if (flag10) { scalarData.IsFlowPlainAllowed = false; scalarData.IsBlockPlainAllowed = false; scalarData.IsSingleQuotedAllowed = false; } if (flag11 || flag16) { scalarData.IsFlowPlainAllowed = false; scalarData.IsBlockPlainAllowed = false; scalarData.IsSingleQuotedAllowed = false; } if (flag18) { scalarData.IsBlockAllowed = false; } scalarData.IsMultiline = flag15; if (flag15) { scalarData.IsFlowPlainAllowed = false; scalarData.IsBlockPlainAllowed = false; } if (flag) { scalarData.IsFlowPlainAllowed = false; } if (flag2) { scalarData.IsBlockPlainAllowed = false; } scalarData.HasSingleQuotes = flag17; } finally { ((IDisposable)bufferWrapper/*cast due to .constrained prefix*/).Dispose(); } } private bool ValueIsRepresentableInOutputEncoding(string value) { if (outputUsesUnicodeEncoding) { return true; } try { byte[] bytes = output.Encoding.GetBytes(value); string text = output.Encoding.GetString(bytes, 0, bytes.Length); return text.Equals(value); } catch (EncoderFallbackException) { return false; } catch (ArgumentOutOfRangeException) { return false; } } private static bool IsUnicode(Encoding encoding) { if (!(encoding is UTF8Encoding) && !(encoding is UnicodeEncoding)) { return encoding is UTF7Encoding; } return true; } private void AnalyzeTag(TagName tag) { tagData.Handle = tag.Value; foreach (TagDirective tagDirective in tagDirectives) { if (tag.Value.StartsWith(tagDirective.Prefix, StringComparison.Ordinal)) { tagData.Handle = tagDirective.Handle; tagData.Suffix = tag.Value.Substring(tagDirective.Prefix.Length); break; } } } private void StateMachine(ParsingEvent evt) { if (evt is YamlDotNet.Core.Events.Comment comment) { EmitComment(comment); return; } switch (state) { case EmitterState.StreamStart: EmitStreamStart(evt); break; case EmitterState.FirstDocumentStart: EmitDocumentStart(evt, isFirst: true); break; case EmitterState.DocumentStart: EmitDocumentStart(evt, isFirst: false); break; case EmitterState.DocumentContent: EmitDocumentContent(evt); break; case EmitterState.DocumentEnd: EmitDocumentEnd(evt); break; case EmitterState.FlowSequenceFirstItem: EmitFlowSequenceItem(evt, isFirst: true); break; case EmitterState.FlowSequenceItem: EmitFlowSequenceItem(evt, isFirst: false); break; case EmitterState.FlowMappingFirstKey: EmitFlowMappingKey(evt, isFirst: true); break; case EmitterState.FlowMappingKey: EmitFlowMappingKey(evt, isFirst: false); break; case EmitterState.FlowMappingSimpleValue: EmitFlowMappingValue(evt, isSimple: true); break; case EmitterState.FlowMappingValue: EmitFlowMappingValue(evt, isSimple: false); break; case EmitterState.BlockSequenceFirstItem: EmitBlockSequenceItem(evt, isFirst: true); break; case EmitterState.BlockSequenceItem: EmitBlockSequenceItem(evt, isFirst: false); break; case EmitterState.BlockMappingFirstKey: EmitBlockMappingKey(evt, isFirst: true); break; case EmitterState.BlockMappingKey: EmitBlockMappingKey(evt, isFirst: false); break; case EmitterState.BlockMappingSimpleValue: EmitBlockMappingValue(evt, isSimple: true); break; case EmitterState.BlockMappingValue: EmitBlockMappingValue(evt, isSimple: false); break; case EmitterState.StreamEnd: throw new YamlException("Expected nothing after STREAM-END"); default: throw new InvalidOperationException(); } } private void EmitComment(YamlDotNet.Core.Events.Comment comment) { if (flowLevel > 0 || state == EmitterState.FlowMappingFirstKey || state == EmitterState.FlowSequenceFirstItem) { return; } string[] array = comment.Value.Split(NewLineSeparators, StringSplitOptions.None); if (comment.IsInline) { Write(" # "); Write(string.Join(" ", array)); } else { bool flag = state == EmitterState.BlockMappingFirstKey; if (flag) { IncreaseIndent(isFlow: false, isIndentless: false); } string[] array2 = array; foreach (string value in array2) { WriteIndent(); Write("# "); Write(value); WriteBreak(); } if (flag) { indent = indents.Pop(); } } isIndentation = true; } private void EmitStreamStart(ParsingEvent evt) { if (!(evt is YamlDotNet.Core.Events.StreamStart)) { throw new ArgumentException("Expected STREAM-START.", "evt"); } indent = -1; column = 0; isWhitespace = true; isIndentation = true; state = EmitterState.FirstDocumentStart; } private void EmitDocumentStart(ParsingEvent evt, bool isFirst) { if (evt is YamlDotNet.Core.Events.DocumentStart documentStart) { bool flag = documentStart.IsImplicit && isFirst && !isCanonical; TagDirectiveCollection tagDirectiveCollection = NonDefaultTagsAmong(documentStart.Tags); if (!isFirst && !isDocumentEndWritten && (documentStart.Version != null || tagDirectiveCollection.Count > 0)) { isDocumentEndWritten = false; WriteIndicator("...", needWhitespace: true, whitespace: false, indentation: false); WriteIndent(); } if (documentStart.Version != null) { AnalyzeVersionDirective(documentStart.Version); Version version = documentStart.Version.Version; flag = false; WriteIndicator("%YAML", needWhitespace: true, whitespace: false, indentation: false); WriteIndicator(string.Format(CultureInfo.InvariantCulture, "{0}.{1}", version.Major, version.Minor), needWhitespace: true, whitespace: false, indentation: false); WriteIndent(); } foreach (TagDirective item in tagDirectiveCollection) { AppendTagDirectiveTo(item, allowDuplicates: false, tagDirectives); } TagDirective[] defaultTagDirectives = Constants.DefaultTagDirectives; foreach (TagDirective value in defaultTagDirectives) { AppendTagDirectiveTo(value, allowDuplicates: true, tagDirectives); } if (tagDirectiveCollection.Count > 0) { flag = false; TagDirective[] defaultTagDirectives2 = Constants.DefaultTagDirectives; foreach (TagDirective value2 in defaultTagDirectives2) { AppendTagDirectiveTo(value2, allowDuplicates: true, tagDirectiveCollection); } foreach (TagDirective item2 in tagDirectiveCollection) { WriteIndicator("%TAG", needWhitespace: true, whitespace: false, indentation: false); WriteTagHandle(item2.Handle); WriteTagContent(item2.Prefix, needsWhitespace: true); WriteIndent(); } } if (CheckEmptyDocument()) { flag = false; } if (!flag) { WriteIndent(); WriteIndicator("---", needWhitespace: true, whitespace: false, indentation: false); if (isCanonical) { WriteIndent(); } } state = EmitterState.DocumentContent; } else { if (!(evt is YamlDotNet.Core.Events.StreamEnd)) { throw new YamlException("Expected DOCUMENT-START or STREAM-END"); } state = EmitterState.StreamEnd; } } private static TagDirectiveCollection NonDefaultTagsAmong(IEnumerable? tagCollection) { TagDirectiveCollection tagDirectiveCollection = new TagDirectiveCollection(); if (tagCollection == null) { return tagDirectiveCollection; } foreach (TagDirective item2 in tagCollection) { AppendTagDirectiveTo(item2, allowDuplicates: false, tagDirectiveCollection); } TagDirective[] defaultTagDirectives = Constants.DefaultTagDirectives; foreach (TagDirective item in defaultTagDirectives) { tagDirectiveCollection.Remove(item); } return tagDirectiveCollection; } private static void AnalyzeVersionDirective(VersionDirective versionDirective) { if (versionDirective.Version.Major != 1 || versionDirective.Version.Minor > 3) { throw new YamlException("Incompatible %YAML directive"); } } private static void AppendTagDirectiveTo(TagDirective value, bool allowDuplicates, TagDirectiveCollection tagDirectives) { if (tagDirectives.Contains(value)) { if (!allowDuplicates) { throw new YamlException("Duplicate %TAG directive."); } } else { tagDirectives.Add(value); } } private void EmitDocumentContent(ParsingEvent evt) { states.Push(EmitterState.DocumentEnd); EmitNode(evt, isMapping: false, isSimpleKey: false); } private void EmitNode(ParsingEvent evt, bool isMapping, bool isSimpleKey) { isMappingContext = isMapping; isSimpleKeyContext = isSimpleKey; switch (evt.Type) { case EventType.Alias: EmitAlias(); break; case EventType.Scalar: EmitScalar(evt); break; case EventType.SequenceStart: EmitSequenceStart(evt); break; case EventType.MappingStart: EmitMappingStart(evt); break; default: throw new YamlException($"Expected SCALAR, SEQUENCE-START, MAPPING-START, or ALIAS, got {evt.Type}"); } } private void EmitAlias() { ProcessAnchor(); state = states.Pop(); } private void EmitScalar(ParsingEvent evt) { SelectScalarStyle(evt); ProcessAnchor(); ProcessTag(); IncreaseIndent(isFlow: true, isIndentless: false); ProcessScalar(); indent = indents.Pop(); state = states.Pop(); } private void SelectScalarStyle(ParsingEvent evt) { YamlDotNet.Core.Events.Scalar scalar = (YamlDotNet.Core.Events.Scalar)evt; ScalarStyle scalarStyle = scalar.Style; bool flag = tagData.Handle == null && tagData.Suffix == null; if (flag && !scalar.IsPlainImplicit && !scalar.IsQuotedImplicit) { throw new YamlException("Neither tag nor isImplicit flags are specified."); } if (scalarStyle == ScalarStyle.Any) { scalarStyle = ((!scalarData.IsMultiline) ? ScalarStyle.Plain : ScalarStyle.Folded); } if (isCanonical) { scalarStyle = ScalarStyle.DoubleQuoted; } if (isSimpleKeyContext && scalarData.IsMultiline) { scalarStyle = ScalarStyle.DoubleQuoted; } if (scalarStyle == ScalarStyle.Plain) { if ((flowLevel != 0 && !scalarData.IsFlowPlainAllowed) || (flowLevel == 0 && !scalarData.IsBlockPlainAllowed)) { scalarStyle = ((scalarData.IsSingleQuotedAllowed && !scalarData.HasSingleQuotes) ? ScalarStyle.SingleQuoted : ScalarStyle.DoubleQuoted); } if (string.IsNullOrEmpty(scalarData.Value) && (flowLevel != 0 || isSimpleKeyContext)) { scalarStyle = ScalarStyle.SingleQuoted; } if (flag && !scalar.IsPlainImplicit) { scalarStyle = ScalarStyle.SingleQuoted; } } if (scalarStyle == ScalarStyle.SingleQuoted && !scalarData.IsSingleQuotedAllowed) { scalarStyle = ScalarStyle.DoubleQuoted; } if ((scalarStyle == ScalarStyle.Literal || scalarStyle == ScalarStyle.Folded) && (!scalarData.IsBlockAllowed || flowLevel != 0 || isSimpleKeyContext)) { scalarStyle = ScalarStyle.DoubleQuoted; } if (scalarStyle == ScalarStyle.ForcePlain) { scalarStyle = ScalarStyle.Plain; } scalarData.Style = scalarStyle; } private void ProcessScalar() { switch (scalarData.Style) { case ScalarStyle.Plain: WritePlainScalar(scalarData.Value, !isSimpleKeyContext); break; case ScalarStyle.SingleQuoted: WriteSingleQuotedScalar(scalarData.Value, !isSimpleKeyContext); break; case ScalarStyle.DoubleQuoted: WriteDoubleQuotedScalar(scalarData.Value, !isSimpleKeyContext); break; case ScalarStyle.Literal: WriteLiteralScalar(scalarData.Value); break; case ScalarStyle.Folded: WriteFoldedScalar(scalarData.Value); break; default: throw new InvalidOperationException(); } } private void WritePlainScalar(string value, bool allowBreaks) { if (!isWhitespace) { Write(' '); } bool flag = false; bool flag2 = false; for (int i = 0; i < value.Length; i++) { char c = value[i]; if (IsSpace(c)) { if (allowBreaks && !flag && column > bestWidth && i + 1 < value.Length && value[i + 1] != ' ') { WriteIndent(); } else { Write(c); } flag = true; continue; } if (IsBreak(c, out var breakChar)) { if (!flag2 && c == '\n') { WriteBreak(); } WriteBreak(breakChar); isIndentation = true; flag2 = true; continue; } if (flag2) { WriteIndent(); } Write(c); isIndentation = false; flag = false; flag2 = false; } isWhitespace = false; isIndentation = false; } private void WriteSingleQuotedScalar(string value, bool allowBreaks) { WriteIndicator("'", needWhitespace: true, whitespace: false, indentation: false); bool flag = false; bool flag2 = false; for (int i = 0; i < value.Length; i++) { char c = value[i]; if (c == ' ') { if (allowBreaks && !flag && column > bestWidth && i != 0 && i + 1 < value.Length && value[i + 1] != ' ') { WriteIndent(); } else { Write(c); } flag = true; continue; } if (IsBreak(c, out var breakChar)) { if (!flag2 && c == '\n') { WriteBreak(); } WriteBreak(breakChar); isIndentation = true; flag2 = true; continue; } if (flag2) { WriteIndent(); } if (c == '\'') { Write(c); } Write(c); isIndentation = false; flag = false; flag2 = false; } WriteIndicator("'", needWhitespace: false, whitespace: false, indentation: false); isWhitespace = false; isIndentation = false; } private void WriteDoubleQuotedScalar(string value, bool allowBreaks) { WriteIndicator("\"", needWhitespace: true, whitespace: false, indentation: false); bool flag = false; for (int i = 0; i < value.Length; i++) { char c = value[i]; if (IsPrintable(c) && !IsBreak(c, out var _)) { switch (c) { case '"': case '\\': break; case ' ': if (allowBreaks && !flag && column > bestWidth && i > 0 && i + 1 < value.Length) { WriteIndent(); if (value[i + 1] == ' ') { Write('\\'); } } else { Write(c); } flag = true; continue; default: Write(c); flag = false; continue; } } Write('\\'); switch (c) { case '\0': Write('0'); break; case '\a': Write('a'); break; case '\b': Write('b'); break; case '\t': Write('t'); break; case '\n': Write('n'); break; case '\v': Write('v'); break; case '\f': Write('f'); break; case '\r': Write('r'); break; case '\u001b': Write('e'); break; case '"': Write('"'); break; case '\\': Write('\\'); break; case '\u0085': Write('N'); break; case '\u00a0': Write('_'); break; case '\u2028': Write('L'); break; case '\u2029': Write('P'); break; default: { ushort num = c; if (num <= 255) { Write('x'); Write(num.ToString("X02", CultureInfo.InvariantCulture)); } else if (IsHighSurrogate(c)) { if (i + 1 >= value.Length || !IsLowSurrogate(value[i + 1])) { throw new SyntaxErrorException("While writing a quoted scalar, found an orphaned high surrogate."); } if (useUtf16SurrogatePair) { Write('u'); Write(num.ToString("X04", CultureInfo.InvariantCulture)); Write('\\'); Write('u'); Write(((ushort)value[i + 1]).ToString("X04", CultureInfo.InvariantCulture)); } else { Write('U'); Write(char.ConvertToUtf32(c, value[i + 1]).ToString("X08", CultureInfo.InvariantCulture)); } i++; } else { Write('u'); Write(num.ToString("X04", CultureInfo.InvariantCulture)); } break; } } flag = false; } WriteIndicator("\"", needWhitespace: false, whitespace: false, indentation: false); isWhitespace = false; isIndentation = false; } private void WriteLiteralScalar(string value) { bool flag = true; WriteIndicator("|", needWhitespace: true, whitespace: false, indentation: false); WriteBlockScalarHints(value); WriteBreak(); isIndentation = true; isWhitespace = true; for (int i = 0; i < value.Length; i++) { char c = value[i]; if (c == '\r' && i + 1 < value.Length && value[i + 1] == '\n') { continue; } if (IsBreak(c, out var breakChar)) { WriteBreak(breakChar); isIndentation = true; flag = true; continue; } if (flag) { WriteIndent(); } Write(c); isIndentation = false; flag = false; } } private void WriteFoldedScalar(string value) { bool flag = true; bool flag2 = true; WriteIndicator(">", needWhitespace: true, whitespace: false, indentation: false); WriteBlockScalarHints(value); WriteBreak(); isIndentation = true; isWhitespace = true; for (int i = 0; i < value.Length; i++) { char c = value[i]; if (IsBreak(c, out var breakChar)) { if (c == '\r' && i + 1 < value.Length && value[i + 1] == '\n') { continue; } if (!flag && !flag2 && breakChar == '\n') { int j; char breakChar2; for (j = 0; i + j < value.Length && IsBreak(value[i + j], out breakChar2); j++) { } if (i + j < value.Length && !IsBlank(value[i + j]) && !IsBreak(value[i + j], out breakChar2)) { WriteBreak(); } } WriteBreak(breakChar); isIndentation = true; flag = true; } else { if (flag) { WriteIndent(); flag2 = IsBlank(c); } if (!flag && c == ' ' && i + 1 < value.Length && value[i + 1] != ' ' && column > bestWidth) { WriteIndent(); } else { Write(c); } isIndentation = false; flag = false; } } } private static bool IsSpace(char character) { return character == ' '; } private static bool IsBreak(char character, out char breakChar) { switch (character) { case '\n': case '\r': case '\u0085': breakChar = '\n'; return true; case '\u2028': case '\u2029': breakChar = character; return true; default: breakChar = '\0'; return false; } } private static bool IsBlank(char character) { if (character != ' ') { return character == '\t'; } return true; } private static bool IsPrintable(char character) { switch (character) { default: if (character != '\u0085' && (character < '\u00a0' || character > '\ud7ff')) { if (character >= '\ue000') { return character <= '\ufffd'; } return false; } break; case '\t': case '\n': case '\r': case ' ': case '!': case '"': case '#': case '$': case '%': case '&': case '\'': case '(': case ')': case '*': case '+': case ',': case '-': case '.': case '/': case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': case ':': case ';': case '<': case '=': case '>': case '?': case '@': case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G': case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U': case 'V': case 'W': case 'X': case 'Y': case 'Z': case '[': case '\\': case ']': case '^': case '_': case '`': case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g': case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n': case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u': case 'v': case 'w': case 'x': case 'y': case 'z': case '{': case '|': case '}': case '~': break; } return true; } private static bool IsHighSurrogate(char c) { if ('\ud800' <= c) { return c <= '\udbff'; } return false; } private static bool IsLowSurrogate(char c) { if ('\udc00' <= c) { return c <= '\udfff'; } return false; } private void EmitSequenceStart(ParsingEvent evt) { ProcessAnchor(); ProcessTag(); SequenceStart sequenceStart = (SequenceStart)evt; if (flowLevel != 0 || isCanonical || sequenceStart.Style == SequenceStyle.Flow || CheckEmptySequence()) { state = EmitterState.FlowSequenceFirstItem; } else { state = EmitterState.BlockSequenceFirstItem; } } private void EmitMappingStart(ParsingEvent evt) { ProcessAnchor(); ProcessTag(); MappingStart mappingStart = (MappingStart)evt; if (flowLevel != 0 || isCanonical || mappingStart.Style == MappingStyle.Flow || CheckEmptyMapping()) { state = EmitterState.FlowMappingFirstKey; } else { state = EmitterState.BlockMappingFirstKey; } } private void ProcessAnchor() { if (!anchorData.Anchor.IsEmpty && !skipAnchorName) { WriteIndicator(anchorData.IsAlias ? "*" : "&", needWhitespace: true, whitespace: false, indentation: false); WriteAnchor(anchorData.Anchor); } } private void ProcessTag() { if (tagData.Handle == null && tagData.Suffix == null) { return; } if (tagData.Handle != null) { WriteTagHandle(tagData.Handle); if (tagData.Suffix != null) { WriteTagContent(tagData.Suffix, needsWhitespace: false); } } else { WriteIndicator("!<", needWhitespace: true, whitespace: false, indentation: false); WriteTagContent(tagData.Suffix, needsWhitespace: false); WriteIndicator(">", needWhitespace: false, whitespace: false, indentation: false); } } private void EmitDocumentEnd(ParsingEvent evt) { if (evt is YamlDotNet.Core.Events.DocumentEnd documentEnd) { WriteIndent(); if (!documentEnd.IsImplicit) { WriteIndicator("...", needWhitespace: true, whitespace: false, indentation: false); WriteIndent(); isDocumentEndWritten = true; } state = EmitterState.DocumentStart; tagDirectives.Clear(); return; } throw new YamlException("Expected DOCUMENT-END."); } private void EmitFlowSequenceItem(ParsingEvent evt, bool isFirst) { if (isFirst) { WriteIndicator("[", needWhitespace: true, whitespace: true, indentation: false); IncreaseIndent(isFlow: true, isIndentless: false); flowLevel++; } if (evt is SequenceEnd) { flowLevel--; indent = indents.Pop(); if (isCanonical && !isFirst) { WriteIndicator(",", needWhitespace: false, whitespace: false, indentation: false); WriteIndent(); } WriteIndicator("]", needWhitespace: false, whitespace: false, indentation: false); state = states.Pop(); } else { if (!isFirst) { WriteIndicator(",", needWhitespace: false, whitespace: false, indentation: false); } if (isCanonical || column > bestWidth) { WriteIndent(); } states.Push(EmitterState.FlowSequenceItem); EmitNode(evt, isMapping: false, isSimpleKey: false); } } private void EmitFlowMappingKey(ParsingEvent evt, bool isFirst) { if (isFirst) { WriteIndicator("{", needWhitespace: true, whitespace: true, indentation: false); IncreaseIndent(isFlow: true, isIndentless: false); flowLevel++; } if (evt is MappingEnd) { flowLevel--; indent = indents.Pop(); if (isCanonical && !isFirst) { WriteIndicator(",", needWhitespace: false, whitespace: false, indentation: false); WriteIndent(); } WriteIndicator("}", needWhitespace: false, whitespace: false, indentation: false); state = states.Pop(); return; } if (!isFirst) { WriteIndicator(",", needWhitespace: false, whitespace: false, indentation: false); } if (isCanonical || column > bestWidth) { WriteIndent(); } if (!isCanonical && CheckSimpleKey()) { states.Push(EmitterState.FlowMappingSimpleValue); EmitNode(evt, isMapping: true, isSimpleKey: true); } else { WriteIndicator("?", needWhitespace: true, whitespace: false, indentation: false); states.Push(EmitterState.FlowMappingValue); EmitNode(evt, isMapping: true, isSimpleKey: false); } } private void EmitFlowMappingValue(ParsingEvent evt, bool isSimple) { if (isSimple) { WriteIndicator(":", needWhitespace: false, whitespace: false, indentation: false); } else { if (isCanonical || column > bestWidth) { WriteIndent(); } WriteIndicator(":", needWhitespace: true, whitespace: false, indentation: false); } states.Push(EmitterState.FlowMappingKey); EmitNode(evt, isMapping: true, isSimpleKey: false); } private void EmitBlockSequenceItem(ParsingEvent evt, bool isFirst) { if (isFirst) { IncreaseIndent(isFlow: false, isMappingContext && !isIndentation); } if (evt is SequenceEnd) { indent = indents.Pop(); state = states.Pop(); return; } WriteIndent(); WriteIndicator("-", needWhitespace: true, whitespace: false, indentation: true); states.Push(EmitterState.BlockSequenceItem); EmitNode(evt, isMapping: false, isSimpleKey: false); } private void EmitBlockMappingKey(ParsingEvent evt, bool isFirst) { if (isFirst) { IncreaseIndent(isFlow: false, isIndentless: false); } if (evt is MappingEnd) { indent = indents.Pop(); state = states.Pop(); return; } WriteIndent(); if (CheckSimpleKey()) { states.Push(EmitterState.BlockMappingSimpleValue); EmitNode(evt, isMapping: true, isSimpleKey: true); WriteIndicator(":", needWhitespace: false, whitespace: false, indentation: false); } else { WriteIndicator("?", needWhitespace: true, whitespace: false, indentation: true); states.Push(EmitterState.BlockMappingValue); EmitNode(evt, isMapping: true, isSimpleKey: false); } } private void EmitBlockMappingValue(ParsingEvent evt, bool isSimple) { if (!isSimple) { WriteIndent(); WriteIndicator(":", needWhitespace: true, whitespace: false, indentation: true); } states.Push(EmitterState.BlockMappingKey); EmitNode(evt, isMapping: true, isSimpleKey: false); } private void IncreaseIndent(bool isFlow, bool isIndentless) { indents.Push(indent); if (indent < 0) { indent = (isFlow ? bestIndent : 0); } else if (!isIndentless || !forceIndentLess) { indent += bestIndent; } } private bool CheckEmptyDocument() { int num = 0; foreach (ParsingEvent @event in events) { num++; if (num == 2) { if (@event is YamlDotNet.Core.Events.Scalar scalar) { return string.IsNullOrEmpty(scalar.Value); } break; } } return false; } private bool CheckSimpleKey() { if (events.Count < 1) { return false; } int num; switch (events.Peek().Type) { case EventType.Alias: num = AnchorNameLength(anchorData.Anchor); break; case EventType.Scalar: if (scalarData.IsMultiline) { return false; } num = AnchorNameLength(anchorData.Anchor) + SafeStringLength(tagData.Handle) + SafeStringLength(tagData.Suffix) + SafeStringLength(scalarData.Value); break; case EventType.SequenceStart: if (!CheckEmptySequence()) { return false; } num = AnchorNameLength(anchorData.Anchor) + SafeStringLength(tagData.Handle) + SafeStringLength(tagData.Suffix); break; case EventType.MappingStart: if (!CheckEmptySequence()) { return false; } num = AnchorNameLength(anchorData.Anchor) + SafeStringLength(tagData.Handle) + SafeStringLength(tagData.Suffix); break; default: return false; } return num <= maxSimpleKeyLength; } private static int AnchorNameLength(AnchorName value) { if (!value.IsEmpty) { return value.Value.Length; } return 0; } private static int SafeStringLength(string? value) { return value?.Length ?? 0; } private bool CheckEmptySequence() { return CheckEmptyStructure(); } private bool CheckEmptyMapping() { return CheckEmptyStructure(); } private bool CheckEmptyStructure() where TStart : NodeEvent where TEnd : ParsingEvent { if (events.Count < 2) { return false; } using Queue.Enumerator enumerator = events.GetEnumerator(); return enumerator.MoveNext() && enumerator.Current is TStart && enumerator.MoveNext() && enumerator.Current is TEnd; } private void WriteBlockScalarHints(string value) { StringLookAheadBufferPool.BufferWrapper bufferWrapper = StringLookAheadBufferPool.Rent(value); try { CharacterAnalyzer characterAnalyzer = new CharacterAnalyzer(bufferWrapper.Buffer); if (characterAnalyzer.IsSpace() || characterAnalyzer.IsBreak()) { int num = bestIndent; string indicator = num.ToString(CultureInfo.InvariantCulture); WriteIndicator(indicator, needWhitespace: false, whitespace: false, indentation: false); } string text = null; if (value.Length == 0 || !characterAnalyzer.IsBreak(value.Length - 1)) { text = "-"; } else if (value.Length >= 2 && characterAnalyzer.IsBreak(value.Length - 2)) { text = "+"; } if (text != null) { WriteIndicator(text, needWhitespace: false, whitespace: false, indentation: false); } } finally { ((IDisposable)bufferWrapper/*cast due to .constrained prefix*/).Dispose(); } } private void WriteIndicator(string indicator, bool needWhitespace, bool whitespace, bool indentation) { if (needWhitespace && !isWhitespace) { Write(' '); } Write(indicator); isWhitespace = whitespace; isIndentation &= indentation; } private void WriteIndent() { int num = Math.Max(indent, 0); if (!isIndentation || column > num || (column == num && !isWhitespace)) { WriteBreak(); } while (column < num) { Write(' '); } isWhitespace = true; isIndentation = true; } private void WriteAnchor(AnchorName value) { Write(value.Value); isWhitespace = false; isIndentation = false; } private void WriteTagHandle(string value) { if (!isWhitespace) { Write(' '); } Write(value); isWhitespace = false; isIndentation = false; } private void WriteTagContent(string value, bool needsWhitespace) { if (needsWhitespace && !isWhitespace) { Write(' '); } Write(UrlEncode(value)); isWhitespace = false; isIndentation = false; } private static string UrlEncode(string text) { return UriReplacer.Replace(text, delegate(Match match) { StringBuilderPool.BuilderWrapper builderWrapper = StringBuilderPool.Rent(); try { StringBuilder builder = builderWrapper.Builder; byte[] bytes = Encoding.UTF8.GetBytes(match.Value); foreach (byte b in bytes) { builder.AppendFormat(CultureInfo.InvariantCulture, "%{0:X02}", b); } return builder.ToString(); } finally { ((IDisposable)builderWrapper/*cast due to .constrained prefix*/).Dispose(); } }); } private void Write(char value) { output.Write(value); column++; } private void Write(string value) { output.Write(value); column += value.Length; } private void WriteBreak(char breakCharacter = '\n') { if (breakCharacter == '\n') { output.WriteLine(); } else { output.Write(breakCharacter); } column = 0; } } internal sealed class EmitterSettings { public static readonly EmitterSettings Default = new EmitterSettings(); public int BestIndent { get; } = 2; public int BestWidth { get; } = int.MaxValue; public string NewLine { get; } = Environment.NewLine; public bool IsCanonical { get; } public bool SkipAnchorName { get; private set; } public int MaxSimpleKeyLength { get; } = 1024; public bool IndentSequences { get; } public bool UseUtf16SurrogatePairs { get; } public EmitterSettings() { } public EmitterSettings(int bestIndent, int bestWidth, bool isCanonical, int maxSimpleKeyLength, bool skipAnchorName = false, bool indentSequences = false, string? newLine = null, bool useUtf16SurrogatePairs = false) { if (bestIndent < 2 || bestIndent > 9) { throw new ArgumentOutOfRangeException("bestIndent", "BestIndent must be between 2 and 9, inclusive"); } if (bestWidth <= bestIndent * 2) { throw new ArgumentOutOfRangeException("bestWidth", "BestWidth must be greater than BestIndent x 2."); } if (maxSimpleKeyLength < 0) { throw new ArgumentOutOfRangeException("maxSimpleKeyLength", "MaxSimpleKeyLength must be >= 0"); } BestIndent = bestIndent; BestWidth = bestWidth; IsCanonical = isCanonical; MaxSimpleKeyLength = maxSimpleKeyLength; SkipAnchorName = skipAnchorName; IndentSequences = indentSequences; NewLine = newLine ?? Environment.NewLine; UseUtf16SurrogatePairs = useUtf16SurrogatePairs; } public EmitterSettings WithBestIndent(int bestIndent) { return new EmitterSettings(bestIndent, BestWidth, IsCanonical, MaxSimpleKeyLength, SkipAnchorName, IndentSequences, NewLine, UseUtf16SurrogatePairs); } public EmitterSettings WithBestWidth(int bestWidth) { return new EmitterSettings(BestIndent, bestWidth, IsCanonical, MaxSimpleKeyLength, SkipAnchorName, IndentSequences, NewLine, UseUtf16SurrogatePairs); } public EmitterSettings WithMaxSimpleKeyLength(int maxSimpleKeyLength) { return new EmitterSettings(BestIndent, BestWidth, IsCanonical, maxSimpleKeyLength, SkipAnchorName, IndentSequences, NewLine, UseUtf16SurrogatePairs); } public EmitterSettings WithNewLine(string newLine) { return new EmitterSettings(BestIndent, BestWidth, IsCanonical, MaxSimpleKeyLength, SkipAnchorName, IndentSequences, newLine, UseUtf16SurrogatePairs); } public EmitterSettings Canonical() { return new EmitterSettings(BestIndent, BestWidth, isCanonical: true, MaxSimpleKeyLength, SkipAnchorName); } public EmitterSettings WithoutAnchorName() { return new EmitterSettings(BestIndent, BestWidth, IsCanonical, MaxSimpleKeyLength, skipAnchorName: true, IndentSequences, NewLine, UseUtf16SurrogatePairs); } public EmitterSettings WithIndentedSequences() { return new EmitterSettings(BestIndent, BestWidth, IsCanonical, MaxSimpleKeyLength, SkipAnchorName, indentSequences: true, NewLine, UseUtf16SurrogatePairs); } public EmitterSettings WithUtf16SurrogatePairs() { return new EmitterSettings(BestIndent, BestWidth, IsCanonical, MaxSimpleKeyLength, SkipAnchorName, IndentSequences, NewLine, useUtf16SurrogatePairs: true); } } internal enum EmitterState { StreamStart, StreamEnd, FirstDocumentStart, DocumentStart, DocumentContent, DocumentEnd, FlowSequenceFirstItem, FlowSequenceItem, FlowMappingFirstKey, FlowMappingKey, FlowMappingSimpleValue, FlowMappingValue, BlockSequenceFirstItem, BlockSequenceItem, BlockMappingFirstKey, BlockMappingKey, BlockMappingSimpleValue, BlockMappingValue } internal sealed class ForwardAnchorNotSupportedException : YamlException { public ForwardAnchorNotSupportedException(string message) : base(message) { } public ForwardAnchorNotSupportedException(in Mark start, in Mark end, string message) : base(in start, in end, message) { } public ForwardAnchorNotSupportedException(string message, Exception inner) : base(message, inner) { } } internal static class HashCode { public static int CombineHashCodes(int h1, int h2) { return ((h1 << 5) + h1) ^ h2; } public static int CombineHashCodes(int h1, object? o2) { return CombineHashCodes(h1, GetHashCode(o2)); } private static int GetHashCode(object? obj) { return obj?.GetHashCode() ?? 0; } } internal interface IEmitter { void Emit(ParsingEvent @event); } internal interface ILookAheadBuffer { bool EndOfInput { get; } char Peek(int offset); void Skip(int length); } internal sealed class InsertionQueue : IEnumerable, IEnumerable { private const int DefaultInitialCapacity = 128; private T[] items; private int readPtr; private int writePtr; private int mask; private int count; public int Count => count; public int Capacity => items.Length; public InsertionQueue(int initialCapacity = 128) { if (initialCapacity <= 0) { throw new ArgumentOutOfRangeException("initialCapacity", "The initial capacity must be a positive number."); } if (!initialCapacity.IsPowerOfTwo()) { throw new ArgumentException("The initial capacity must be a power of 2.", "initialCapacity"); } items = new T[initialCapacity]; readPtr = initialCapacity / 2; writePtr = initialCapacity / 2; mask = initialCapacity - 1; } public void Enqueue(T item) { ResizeIfNeeded(); items[writePtr] = item; writePtr = (writePtr - 1) & mask; count++; } public T Dequeue() { if (count == 0) { throw new InvalidOperationException("The queue is empty"); } T result = items[readPtr]; readPtr = (readPtr - 1) & mask; count--; return result; } public void Insert(int index, T item) { if (index > count) { throw new InvalidOperationException("Cannot insert outside of the bounds of the queue"); } ResizeIfNeeded(); CalculateInsertionParameters(mask, count, index, ref readPtr, ref writePtr, out var insertPtr, out var copyIndex, out var copyOffset, out var copyLength); if (copyLength != 0) { Array.Copy(items, copyIndex, items, copyIndex + copyOffset, copyLength); } items[insertPtr] = item; count++; } private void ResizeIfNeeded() { int num = items.Length; if (count == num) { T[] destinationArray = new T[num * 2]; int num2 = readPtr + 1; if (num2 > 0) { Array.Copy(items, 0, destinationArray, 0, num2); } writePtr += num; int num3 = num - num2; if (num3 > 0) { Array.Copy(items, readPtr + 1, destinationArray, writePtr + 1, num3); } items = destinationArray; mask = mask * 2 + 1; } } internal static void CalculateInsertionParameters(int mask, int count, int index, ref int readPtr, ref int writePtr, out int insertPtr, out int copyIndex, out int copyOffset, out int copyLength) { int num = (readPtr + 1) & mask; if (index == 0) { insertPtr = (readPtr = num); copyIndex = 0; copyOffset = 0; copyLength = 0; return; } insertPtr = (readPtr - index) & mask; if (index == count) { writePtr = (writePtr - 1) & mask; copyIndex = 0; copyOffset = 0; copyLength = 0; return; } int num2 = ((num >= insertPtr) ? (readPtr - insertPtr) : int.MaxValue); int num3 = ((writePtr <= insertPtr) ? (insertPtr - writePtr) : int.MaxValue); if (num2 <= num3) { insertPtr++; readPtr++; copyIndex = insertPtr; copyOffset = 1; copyLength = num2; } else { copyIndex = writePtr + 1; copyOffset = -1; copyLength = num3; writePtr = (writePtr - 1) & mask; } } public IEnumerator GetEnumerator() { int ptr = readPtr; for (int i = 0; i < Count; i++) { yield return items[ptr]; ptr = (ptr - 1) & mask; } } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } } internal interface IParser { ParsingEvent? Current { get; } bool MoveNext(); } internal interface IScanner { Mark CurrentPosition { get; } Token? Current { get; } bool MoveNext(); bool MoveNextWithoutConsuming(); void ConsumeCurrent(); } [DebuggerStepThrough] internal sealed class LookAheadBuffer : ILookAheadBuffer { private readonly TextReader input; private readonly char[] buffer; private readonly int blockSize; private readonly int mask; private int firstIndex; private int writeOffset; private int count; private bool endOfInput; public bool EndOfInput { get { if (endOfInput) { return count == 0; } return false; } } public LookAheadBuffer(TextReader input, int capacity) { if (capacity < 1) { throw new ArgumentOutOfRangeException("capacity", "The capacity must be positive."); } if (!capacity.IsPowerOfTwo()) { throw new ArgumentException("The capacity must be a power of 2.", "capacity"); } this.input = input ?? throw new ArgumentNullException("input"); blockSize = capacity; buffer = new char[capacity * 2]; mask = capacity * 2 - 1; } private int GetIndexForOffset(int offset) { return (firstIndex + offset) & mask; } public char Peek(int offset) { if (offset >= count) { FillBuffer(); } if (offset < count) { return buffer[(firstIndex + offset) & mask]; } return '\0'; } public void Cache(int length) { if (length >= count) { FillBuffer(); } } private void FillBuffer() { if (endOfInput) { return; } int num = blockSize; do { int num2 = input.Read(buffer, writeOffset, num); if (num2 == 0) { endOfInput = true; return; } num -= num2; writeOffset += num2; count += num2; } while (num > 0); if (writeOffset == buffer.Length) { writeOffset = 0; } } public void Skip(int length) { if (length < 1 || length > blockSize) { throw new ArgumentOutOfRangeException("length", "The length must be between 1 and the number of characters in the buffer. Use the Peek() and / or Cache() methods to fill the buffer."); } firstIndex = GetIndexForOffset(length); count -= length; } } internal readonly struct Mark : IEquatable, IComparable, IComparable { public static readonly Mark Empty = new Mark(0L, 1L, 1L); public long Index { get; } public long Line { get; } public long Column { get; } public Mark(long index, long line, long column) { if (index < 0) { ThrowHelper.ThrowArgumentOutOfRangeException("index", "Index must be greater than or equal to zero."); } if (line < 1) { ThrowHelper.ThrowArgumentOutOfRangeException("line", "Line must be greater than or equal to 1."); } if (column < 1) { ThrowHelper.ThrowArgumentOutOfRangeException("column", "Column must be greater than or equal to 1."); } Index = index; Line = line; Column = column; } public override string ToString() { return $"Line: {Line}, Col: {Column}, Idx: {Index}"; } public override bool Equals(object? obj) { return Equals((Mark)(obj ?? ((object)Empty))); } public bool Equals(Mark other) { if (Index == other.Index && Line == other.Line) { return Column == other.Column; } return false; } public override int GetHashCode() { return HashCode.CombineHashCodes(Index.GetHashCode(), HashCode.CombineHashCodes(Line.GetHashCode(), Column.GetHashCode())); } public int CompareTo(object? obj) { return CompareTo((Mark)(obj ?? ((object)Empty))); } public int CompareTo(Mark other) { int num = Line.CompareTo(other.Line); if (num == 0) { num = Column.CompareTo(other.Column); } return num; } public static bool operator ==(Mark left, Mark right) { return left.Equals(right); } public static bool operator !=(Mark left, Mark right) { return !(left == right); } public static bool operator <(Mark left, Mark right) { return left.CompareTo(right) < 0; } public static bool operator <=(Mark left, Mark right) { return left.CompareTo(right) <= 0; } public static bool operator >(Mark left, Mark right) { return left.CompareTo(right) > 0; } public static bool operator >=(Mark left, Mark right) { return left.CompareTo(right) >= 0; } } internal sealed class MaximumRecursionLevelReachedException : YamlException { public MaximumRecursionLevelReachedException(string message) : base(message) { } public MaximumRecursionLevelReachedException(in Mark start, in Mark end, string message) : base(in start, in end, message) { } public MaximumRecursionLevelReachedException(string message, Exception inner) : base(message, inner) { } } internal sealed class MergingParser : IParser { private sealed class ParsingEventCollection : IEnumerable>, IEnumerable { private readonly LinkedList events; private readonly HashSet> deleted; private readonly Dictionary> references; public ParsingEventCollection() { events = new LinkedList(); deleted = new HashSet>(); references = new Dictionary>(); } public void AddAfter(LinkedListNode node, IEnumerable items) { foreach (ParsingEvent item in items) { node = events.AddAfter(node, item); } } public void Add(ParsingEvent item) { LinkedListNode node = events.AddLast(item); AddReference(item, node); } public void MarkDeleted(LinkedListNode node) { deleted.Add(node); } public bool IsDeleted(LinkedListNode node) { return deleted.Contains(node); } public void CleanMarked() { foreach (LinkedListNode item in deleted) { events.Remove(item); } } public IEnumerable> FromAnchor(AnchorName anchor) { LinkedListNode next = references[anchor].Next; return Enumerate(next); } public IEnumerator> GetEnumerator() { return Enumerate(events.First).GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } private static IEnumerable> Enumerate(LinkedListNode? node) { while (node != null) { yield return node; node = node.Next; } } private void AddReference(ParsingEvent item, LinkedListNode node) { if (item is MappingStart { Anchor: { IsEmpty: false } anchor }) { references[anchor] = node; } } } private sealed class ParsingEventCloner : IParsingEventVisitor { private ParsingEvent? clonedEvent; public ParsingEvent Clone(ParsingEvent e) { e.Accept(this); if (clonedEvent == null) { throw new InvalidOperationException($"Could not clone event of type '{e.Type}'"); } return clonedEvent; } void IParsingEventVisitor.Visit(YamlDotNet.Core.Events.AnchorAlias e) { clonedEvent = new YamlDotNet.Core.Events.AnchorAlias(e.Value, e.Start, e.End); } void IParsingEventVisitor.Visit(YamlDotNet.Core.Events.StreamStart e) { throw new NotSupportedException(); } void IParsingEventVisitor.Visit(YamlDotNet.Core.Events.StreamEnd e) { throw new NotSupportedException(); } void IParsingEventVisitor.Visit(YamlDotNet.Core.Events.DocumentStart e) { throw new NotSupportedException(); } void IParsingEventVisitor.Visit(YamlDotNet.Core.Events.DocumentEnd e) { throw new NotSupportedException(); } void IParsingEventVisitor.Visit(YamlDotNet.Core.Events.Scalar e) { clonedEvent = new YamlDotNet.Core.Events.Scalar(AnchorName.Empty, e.Tag, e.Value, e.Style, e.IsPlainImplicit, e.IsQuotedImplicit, e.Start, e.End); } void IParsingEventVisitor.Visit(SequenceStart e) { clonedEvent = new SequenceStart(AnchorName.Empty, e.Tag, e.IsImplicit, e.Style, e.Start, e.End); } void IParsingEventVisitor.Visit(SequenceEnd e) { clonedEvent = new SequenceEnd(e.Start, e.End); } void IParsingEventVisitor.Visit(MappingStart e) { clonedEvent = new MappingStart(AnchorName.Empty, e.Tag, e.IsImplicit, e.Style, e.Start, e.End); } void IParsingEventVisitor.Visit(MappingEnd e) { clonedEvent = new MappingEnd(e.Start, e.End); } void IParsingEventVisitor.Visit(YamlDotNet.Core.Events.Comment e) { throw new NotSupportedException(); } } private readonly ParsingEventCollection events; private readonly IParser innerParser; private IEnumerator> iterator; private bool merged; public ParsingEvent? Current => iterator.Current?.Value; public MergingParser(IParser innerParser) { events = new ParsingEventCollection(); merged = false; iterator = events.GetEnumerator(); this.innerParser = innerParser; } public bool MoveNext() { if (!merged) { Merge(); events.CleanMarked(); iterator = events.GetEnumerator(); merged = true; } return iterator.MoveNext(); } private void Merge() { while (innerParser.MoveNext()) { events.Add(innerParser.Current); } foreach (LinkedListNode @event in events) { if (IsMergeToken(@event)) { events.MarkDeleted(@event); if (!HandleMerge(@event.Next)) { throw new SemanticErrorException(@event.Value.Start, @event.Value.End, "Unrecognized merge key pattern"); } } } } private bool HandleMerge(LinkedListNode? node) { if (node == null) { return false; } if (node.Value is YamlDotNet.Core.Events.AnchorAlias anchorAlias) { return HandleAnchorAlias(node, node, anchorAlias); } if (node.Value is SequenceStart) { return HandleSequence(node); } return false; } private bool HandleMergeSequence(LinkedListNode sequenceStart, LinkedListNode? node) { if (node == null) { return false; } if (node.Value is YamlDotNet.Core.Events.AnchorAlias anchorAlias) { return HandleAnchorAlias(sequenceStart, node, anchorAlias); } if (node.Value is SequenceStart) { return HandleSequence(node); } return false; } private static bool IsMergeToken(LinkedListNode node) { if (node.Value is YamlDotNet.Core.Events.Scalar scalar) { return scalar.Value == "<<"; } return false; } private bool HandleAnchorAlias(LinkedListNode node, LinkedListNode anchorNode, YamlDotNet.Core.Events.AnchorAlias anchorAlias) { IEnumerable mappingEvents = GetMappingEvents(anchorAlias.Value); events.AddAfter(node, mappingEvents); events.MarkDeleted(anchorNode); return true; } private bool HandleSequence(LinkedListNode node) { events.MarkDeleted(node); LinkedListNode linkedListNode = node; while (linkedListNode != null) { if (linkedListNode.Value is SequenceEnd) { events.MarkDeleted(linkedListNode); return true; } LinkedListNode next = linkedListNode.Next; HandleMergeSequence(node, next); linkedListNode = next; } return true; } private IEnumerable GetMappingEvents(AnchorName anchor) { ParsingEventCloner parsingEventCloner = new ParsingEventCloner(); int nesting = 0; return (from e in events.FromAnchor(anchor) where !events.IsDeleted(e) select e.Value).TakeWhile((ParsingEvent e) => (nesting += e.NestingIncrease) >= 0).Select(parsingEventCloner.Clone); } } internal class Parser : IParser { private class EventQueue { private readonly Queue highPriorityEvents = new Queue(); private readonly Queue normalPriorityEvents = new Queue(); public int Count => highPriorityEvents.Count + normalPriorityEvents.Count; public void Enqueue(ParsingEvent @event) { EventType type = @event.Type; if (type == EventType.StreamStart || type == EventType.DocumentStart) { highPriorityEvents.Enqueue(@event); } else { normalPriorityEvents.Enqueue(@event); } } public ParsingEvent Dequeue() { if (highPriorityEvents.Count <= 0) { return normalPriorityEvents.Dequeue(); } return highPriorityEvents.Dequeue(); } } private readonly Stack states = new Stack(); private readonly TagDirectiveCollection tagDirectives = new TagDirectiveCollection(); private ParserState state; private readonly IScanner scanner; private Token? currentToken; private VersionDirective? version; private readonly EventQueue pendingEvents = new EventQueue(); public ParsingEvent? Current { get; private set; } private Token? GetCurrentToken() { if (currentToken == null) { while (scanner.MoveNextWithoutConsuming()) { currentToken = scanner.Current; if (!(currentToken is YamlDotNet.Core.Tokens.Comment comment)) { break; } pendingEvents.Enqueue(new YamlDotNet.Core.Events.Comment(comment.Value, comment.IsInline, comment.Start, comment.End)); scanner.ConsumeCurrent(); } } return currentToken; } public Parser(TextReader input) : this(new Scanner(input)) { } public Parser(IScanner scanner) { this.scanner = scanner; } public bool MoveNext() { if (state == ParserState.StreamEnd) { Current = null; return false; } if (pendingEvents.Count == 0) { pendingEvents.Enqueue(StateMachine()); } Current = pendingEvents.Dequeue(); return true; } private ParsingEvent StateMachine() { return state switch { ParserState.StreamStart => ParseStreamStart(), ParserState.ImplicitDocumentStart => ParseDocumentStart(isImplicit: true), ParserState.DocumentStart => ParseDocumentStart(isImplicit: false), ParserState.DocumentContent => ParseDocumentContent(), ParserState.DocumentEnd => ParseDocumentEnd(), ParserState.BlockNode => ParseNode(isBlock: true, isIndentlessSequence: false), ParserState.BlockNodeOrIndentlessSequence => ParseNode(isBlock: true, isIndentlessSequence: true), ParserState.FlowNode => ParseNode(isBlock: false, isIndentlessSequence: false), ParserState.BlockSequenceFirstEntry => ParseBlockSequenceEntry(isFirst: true), ParserState.BlockSequenceEntry => ParseBlockSequenceEntry(isFirst: false), ParserState.IndentlessSequenceEntry => ParseIndentlessSequenceEntry(), ParserState.BlockMappingFirstKey => ParseBlockMappingKey(isFirst: true), ParserState.BlockMappingKey => ParseBlockMappingKey(isFirst: false), ParserState.BlockMappingValue => ParseBlockMappingValue(), ParserState.FlowSequenceFirstEntry => ParseFlowSequenceEntry(isFirst: true), ParserState.FlowSequenceEntry => ParseFlowSequenceEntry(isFirst: false), ParserState.FlowSequenceEntryMappingKey => ParseFlowSequenceEntryMappingKey(), ParserState.FlowSequenceEntryMappingValue => ParseFlowSequenceEntryMappingValue(), ParserState.FlowSequenceEntryMappingEnd => ParseFlowSequenceEntryMappingEnd(), ParserState.FlowMappingFirstKey => ParseFlowMappingKey(isFirst: true), ParserState.FlowMappingKey => ParseFlowMappingKey(isFirst: false), ParserState.FlowMappingValue => ParseFlowMappingValue(isEmpty: false), ParserState.FlowMappingEmptyValue => ParseFlowMappingValue(isEmpty: true), _ => throw new InvalidOperationException(), }; } private void Skip() { if (currentToken != null) { currentToken = null; scanner.ConsumeCurrent(); } } private YamlDotNet.Core.Events.StreamStart ParseStreamStart() { Token token = GetCurrentToken(); if (!(token is YamlDotNet.Core.Tokens.StreamStart streamStart)) { throw new SemanticErrorException(token?.Start ?? Mark.Empty, token?.End ?? Mark.Empty, "Did not find expected ."); } Skip(); state = ParserState.ImplicitDocumentStart; return new YamlDotNet.Core.Events.StreamStart(streamStart.Start, streamStart.End); } private ParsingEvent ParseDocumentStart(bool isImplicit) { if (currentToken is VersionDirective) { throw new SyntaxErrorException("While parsing a document start node, could not find document end marker before version directive."); } Token token = GetCurrentToken(); if (!isImplicit) { while (token is YamlDotNet.Core.Tokens.DocumentEnd) { Skip(); token = GetCurrentToken(); } } if (token == null) { throw new SyntaxErrorException("Reached the end of the stream while parsing a document start."); } if (token is YamlDotNet.Core.Tokens.Scalar && (state == ParserState.ImplicitDocumentStart || state == ParserState.DocumentStart)) { isImplicit = true; } if ((isImplicit && !(token is VersionDirective) && !(token is TagDirective) && !(token is YamlDotNet.Core.Tokens.DocumentStart) && !(token is YamlDotNet.Core.Tokens.StreamEnd) && !(token is YamlDotNet.Core.Tokens.DocumentEnd)) || token is BlockMappingStart) { TagDirectiveCollection tags = new TagDirectiveCollection(); ProcessDirectives(tags); states.Push(ParserState.DocumentEnd); state = ParserState.BlockNode; return new YamlDotNet.Core.Events.DocumentStart(null, tags, isImplicit: true, token.Start, token.End); } if (!(token is YamlDotNet.Core.Tokens.StreamEnd) && !(token is YamlDotNet.Core.Tokens.DocumentEnd)) { Mark start = token.Start; TagDirectiveCollection tags2 = new TagDirectiveCollection(); VersionDirective versionDirective = ProcessDirectives(tags2); token = GetCurrentToken() ?? throw new SemanticErrorException("Reached the end of the stream while parsing a document start"); if (!(token is YamlDotNet.Core.Tokens.DocumentStart)) { throw new SemanticErrorException(token.Start, token.End, "Did not find expected ."); } states.Push(ParserState.DocumentEnd); state = ParserState.DocumentContent; Mark end = token.End; Skip(); return new YamlDotNet.Core.Events.DocumentStart(versionDirective, tags2, isImplicit: false, start, end); } if (token is YamlDotNet.Core.Tokens.DocumentEnd) { Skip(); } state = ParserState.StreamEnd; token = GetCurrentToken() ?? throw new SemanticErrorException("Reached the end of the stream while parsing a document start"); YamlDotNet.Core.Events.StreamEnd result = new YamlDotNet.Core.Events.StreamEnd(token.Start, token.End); if (scanner.MoveNextWithoutConsuming()) { throw new InvalidOperationException("The scanner should contain no more tokens."); } return result; } private VersionDirective? ProcessDirectives(TagDirectiveCollection tags) { bool flag = false; VersionDirective result = null; while (true) { if (GetCurrentToken() is VersionDirective versionDirective) { if (version != null) { throw new SemanticErrorException(versionDirective.Start, versionDirective.End, "Found duplicate %YAML directive."); } if (versionDirective.Version.Major != 1 || versionDirective.Version.Minor > 3) { throw new SemanticErrorException(versionDirective.Start, versionDirective.End, "Found incompatible YAML document."); } result = (version = versionDirective); flag = true; } else { if (!(GetCurrentToken() is TagDirective tagDirective)) { break; } if (tags.Contains(tagDirective.Handle)) { throw new SemanticErrorException(tagDirective.Start, tagDirective.End, "Found duplicate %TAG directive."); } tags.Add(tagDirective); flag = true; } Skip(); } if (GetCurrentToken() is YamlDotNet.Core.Tokens.DocumentStart && (version == null || (version.Version.Major == 1 && version.Version.Minor > 1))) { if (GetCurrentToken() is YamlDotNet.Core.Tokens.DocumentStart && version == null) { version = new VersionDirective(new Version(1, 2)); } flag = true; } AddTagDirectives(tags, Constants.DefaultTagDirectives); if (flag) { tagDirectives.Clear(); } AddTagDirectives(tagDirectives, tags); return result; } private static void AddTagDirectives(TagDirectiveCollection directives, IEnumerable source) { foreach (TagDirective item in source) { if (!directives.Contains(item)) { directives.Add(item); } } } private ParsingEvent ParseDocumentContent() { if (GetCurrentToken() is VersionDirective || GetCurrentToken() is TagDirective || GetCurrentToken() is YamlDotNet.Core.Tokens.DocumentStart || GetCurrentToken() is YamlDotNet.Core.Tokens.DocumentEnd || GetCurrentToken() is YamlDotNet.Core.Tokens.StreamEnd) { state = states.Pop(); return ProcessEmptyScalar(scanner.CurrentPosition); } return ParseNode(isBlock: true, isIndentlessSequence: false); } private static YamlDotNet.Core.Events.Scalar ProcessEmptyScalar(in Mark position) { return new YamlDotNet.Core.Events.Scalar(AnchorName.Empty, TagName.Empty, string.Empty, ScalarStyle.Plain, isPlainImplicit: true, isQuotedImplicit: false, position, position); } private ParsingEvent ParseNode(bool isBlock, bool isIndentlessSequence) { if (GetCurrentToken() is Error { Start: var start } error) { throw new SemanticErrorException(in start, error.End, error.Value); } Token token = GetCurrentToken() ?? throw new SemanticErrorException("Reached the end of the stream while parsing a node"); if (token is YamlDotNet.Core.Tokens.AnchorAlias anchorAlias) { state = states.Pop(); ParsingEvent result = new YamlDotNet.Core.Events.AnchorAlias(anchorAlias.Value, anchorAlias.Start, anchorAlias.End); Skip(); return result; } Mark start2 = token.Start; AnchorName anchor = AnchorName.Empty; TagName tag = TagName.Empty; Anchor anchor2 = null; Tag tag2 = null; while (true) { if (anchor.IsEmpty && token is Anchor anchor3) { anchor2 = anchor3; anchor = anchor3.Value; Skip(); } else { if (!tag.IsEmpty || !(token is Tag tag3)) { if (token is Anchor { Start: var start3 } anchor4) { throw new SemanticErrorException(in start3, anchor4.End, "While parsing a node, found more than one anchor."); } if (token is YamlDotNet.Core.Tokens.AnchorAlias { Start: var start4 } anchorAlias2) { throw new SemanticErrorException(in start4, anchorAlias2.End, "While parsing a node, did not find expected token."); } if (!(token is Error error2)) { break; } if (tag2 != null && anchor2 != null && !anchor.IsEmpty) { return new YamlDotNet.Core.Events.Scalar(anchor, default(TagName), string.Empty, ScalarStyle.Any, isPlainImplicit: false, isQuotedImplicit: false, anchor2.Start, anchor2.End); } throw new SemanticErrorException(error2.Start, error2.End, error2.Value); } tag2 = tag3; if (string.IsNullOrEmpty(tag3.Handle)) { tag = new TagName(tag3.Suffix); } else { if (!tagDirectives.Contains(tag3.Handle)) { throw new SemanticErrorException(tag3.Start, tag3.End, "While parsing a node, found undefined tag handle."); } tag = new TagName(tagDirectives[tag3.Handle].Prefix + tag3.Suffix); } Skip(); } token = GetCurrentToken() ?? throw new SemanticErrorException("Reached the end of the stream while parsing a node"); } bool isEmpty = tag.IsEmpty; if (isIndentlessSequence && GetCurrentToken() is BlockEntry) { state = ParserState.IndentlessSequenceEntry; return new SequenceStart(anchor, tag, isEmpty, SequenceStyle.Block, start2, token.End); } if (token is YamlDotNet.Core.Tokens.Scalar scalar) { bool isPlainImplicit = false; bool isQuotedImplicit = false; if ((scalar.Style == ScalarStyle.Plain && tag.IsEmpty) || tag.IsNonSpecific) { isPlainImplicit = true; } else if (tag.IsEmpty) { isQuotedImplicit = true; } state = states.Pop(); Skip(); ParsingEvent result2 = new YamlDotNet.Core.Events.Scalar(anchor, tag, scalar.Value, scalar.Style, isPlainImplicit, isQuotedImplicit, start2, scalar.End, scalar.IsKey); if (!anchor.IsEmpty && scanner.MoveNextWithoutConsuming()) { currentToken = scanner.Current; if (currentToken is Error) { Error error3 = currentToken as Error; throw new SemanticErrorException(error3.Start, error3.End, error3.Value); } } if (state == ParserState.FlowMappingKey && !(scanner.Current is FlowMappingEnd) && scanner.MoveNextWithoutConsuming()) { currentToken = scanner.Current; if (currentToken != null && !(currentToken is FlowEntry) && !(currentToken is FlowMappingEnd)) { throw new SemanticErrorException(currentToken.Start, currentToken.End, "While parsing a flow mapping, did not find expected ',' or '}'."); } } return result2; } if (token is FlowSequenceStart flowSequenceStart) { state = ParserState.FlowSequenceFirstEntry; return new SequenceStart(anchor, tag, isEmpty, SequenceStyle.Flow, start2, flowSequenceStart.End); } if (token is FlowMappingStart flowMappingStart) { state = ParserState.FlowMappingFirstKey; return new MappingStart(anchor, tag, isEmpty, MappingStyle.Flow, start2, flowMappingStart.End); } if (isBlock) { if (token is BlockSequenceStart blockSequenceStart) { state = ParserState.BlockSequenceFirstEntry; return new SequenceStart(anchor, tag, isEmpty, SequenceStyle.Block, start2, blockSequenceStart.End); } if (token is BlockMappingStart blockMappingStart) { state = ParserState.BlockMappingFirstKey; return new MappingStart(anchor, tag, isEmpty, MappingStyle.Block, start2, blockMappingStart.End); } } if (!anchor.IsEmpty || !tag.IsEmpty) { state = states.Pop(); return new YamlDotNet.Core.Events.Scalar(anchor, tag, string.Empty, ScalarStyle.Plain, isEmpty, isQuotedImplicit: false, start2, token.End); } throw new SemanticErrorException(token.Start, token.End, "While parsing a node, did not find expected node content."); } private YamlDotNet.Core.Events.DocumentEnd ParseDocumentEnd() { Token token = GetCurrentToken() ?? throw new SemanticErrorException("Reached the end of the stream while parsing a document end"); bool isImplicit = true; Mark start = token.Start; Mark end = start; if (token is YamlDotNet.Core.Tokens.DocumentEnd) { end = token.End; Skip(); isImplicit = false; } else if (!(currentToken is YamlDotNet.Core.Tokens.StreamEnd) && !(currentToken is YamlDotNet.Core.Tokens.DocumentStart) && !(currentToken is FlowSequenceEnd) && !(currentToken is VersionDirective) && (!(Current is YamlDotNet.Core.Events.Scalar) || !(currentToken is Error))) { throw new SemanticErrorException(in start, in end, "Did not find expected ."); } if (version != null && version.Version.Major == 1 && version.Version.Minor > 1) { version = null; } state = ParserState.DocumentStart; return new YamlDotNet.Core.Events.DocumentEnd(isImplicit, start, end); } private ParsingEvent ParseBlockSequenceEntry(bool isFirst) { if (isFirst) { GetCurrentToken(); Skip(); } Token token = GetCurrentToken(); if (token is BlockEntry { End: var position }) { Skip(); token = GetCurrentToken(); if (!(token is BlockEntry) && !(token is BlockEnd)) { states.Push(ParserState.BlockSequenceEntry); return ParseNode(isBlock: true, isIndentlessSequence: false); } state = ParserState.BlockSequenceEntry; return ProcessEmptyScalar(in position); } if (token is BlockEnd blockEnd) { state = states.Pop(); ParsingEvent result = new SequenceEnd(blockEnd.Start, blockEnd.End); Skip(); return result; } throw new SemanticErrorException(token?.Start ?? Mark.Empty, token?.End ?? Mark.Empty, "While parsing a block collection, did not find expected '-' indicator."); } private ParsingEvent ParseIndentlessSequenceEntry() { Token token = GetCurrentToken(); if (token is BlockEntry { End: var position }) { Skip(); token = GetCurrentToken(); if (!(token is BlockEntry) && !(token is Key) && !(token is Value) && !(token is BlockEnd)) { states.Push(ParserState.IndentlessSequenceEntry); return ParseNode(isBlock: true, isIndentlessSequence: false); } state = ParserState.IndentlessSequenceEntry; return ProcessEmptyScalar(in position); } state = states.Pop(); return new SequenceEnd(token?.Start ?? Mark.Empty, token?.End ?? Mark.Empty); } private ParsingEvent ParseBlockMappingKey(bool isFirst) { if (isFirst) { GetCurrentToken(); Skip(); } Token token = GetCurrentToken(); if (token is Key { End: var position }) { Skip(); token = GetCurrentToken(); if (!(token is Key) && !(token is Value) && !(token is BlockEnd)) { states.Push(ParserState.BlockMappingValue); return ParseNode(isBlock: true, isIndentlessSequence: true); } state = ParserState.BlockMappingValue; return ProcessEmptyScalar(in position); } if (token is Value value) { Skip(); return ProcessEmptyScalar(value.End); } if (token is YamlDotNet.Core.Tokens.AnchorAlias anchorAlias) { Skip(); return new YamlDotNet.Core.Events.AnchorAlias(anchorAlias.Value, anchorAlias.Start, anchorAlias.End); } if (token is BlockEnd blockEnd) { state = states.Pop(); ParsingEvent result = new MappingEnd(blockEnd.Start, blockEnd.End); Skip(); return result; } if (GetCurrentToken() is Error { Start: var start } error) { throw new SyntaxErrorException(in start, error.End, error.Value); } throw new SemanticErrorException(token?.Start ?? Mark.Empty, token?.End ?? Mark.Empty, "While parsing a block mapping, did not find expected key."); } private ParsingEvent ParseBlockMappingValue() { Token token = GetCurrentToken(); if (token is Value { End: var position }) { Skip(); token = GetCurrentToken(); if (!(token is Key) && !(token is Value) && !(token is BlockEnd)) { states.Push(ParserState.BlockMappingKey); return ParseNode(isBlock: true, isIndentlessSequence: true); } state = ParserState.BlockMappingKey; return ProcessEmptyScalar(in position); } if (token is Error { Start: var start } error) { throw new SemanticErrorException(in start, error.End, error.Value); } state = ParserState.BlockMappingKey; return ProcessEmptyScalar(token?.Start ?? Mark.Empty); } private ParsingEvent ParseFlowSequenceEntry(bool isFirst) { if (isFirst) { GetCurrentToken(); Skip(); } Token token = GetCurrentToken(); ParsingEvent result; if (!(token is FlowSequenceEnd)) { if (!isFirst) { if (!(token is FlowEntry)) { throw new SemanticErrorException(token?.Start ?? Mark.Empty, token?.End ?? Mark.Empty, "While parsing a flow sequence, did not find expected ',' or ']'."); } Skip(); token = GetCurrentToken(); } if (token is Key) { state = ParserState.FlowSequenceEntryMappingKey; result = new MappingStart(AnchorName.Empty, TagName.Empty, isImplicit: true, MappingStyle.Flow); Skip(); return result; } if (!(token is FlowSequenceEnd)) { states.Push(ParserState.FlowSequenceEntry); return ParseNode(isBlock: false, isIndentlessSequence: false); } } state = states.Pop(); result = new SequenceEnd(token?.Start ?? Mark.Empty, token?.End ?? Mark.Empty); Skip(); return result; } private ParsingEvent ParseFlowSequenceEntryMappingKey() { Token token = GetCurrentToken(); if (!(token is Value) && !(token is FlowEntry) && !(token is FlowSequenceEnd)) { states.Push(ParserState.FlowSequenceEntryMappingValue); return ParseNode(isBlock: false, isIndentlessSequence: false); } Mark position = token?.End ?? Mark.Empty; Skip(); state = ParserState.FlowSequenceEntryMappingValue; return ProcessEmptyScalar(in position); } private ParsingEvent ParseFlowSequenceEntryMappingValue() { Token token = GetCurrentToken(); if (token is Value) { Skip(); token = GetCurrentToken(); if (!(token is FlowEntry) && !(token is FlowSequenceEnd)) { states.Push(ParserState.FlowSequenceEntryMappingEnd); return ParseNode(isBlock: false, isIndentlessSequence: false); } } state = ParserState.FlowSequenceEntryMappingEnd; return ProcessEmptyScalar(token?.Start ?? Mark.Empty); } private MappingEnd ParseFlowSequenceEntryMappingEnd() { state = ParserState.FlowSequenceEntry; Token token = GetCurrentToken(); return new MappingEnd(token?.Start ?? Mark.Empty, token?.End ?? Mark.Empty); } private ParsingEvent ParseFlowMappingKey(bool isFirst) { if (isFirst) { GetCurrentToken(); Skip(); } Token token = GetCurrentToken(); if (!(token is FlowMappingEnd)) { if (!isFirst) { if (token is FlowEntry) { Skip(); token = GetCurrentToken(); } else if (!(token is YamlDotNet.Core.Tokens.Scalar)) { throw new SemanticErrorException(token?.Start ?? Mark.Empty, token?.End ?? Mark.Empty, "While parsing a flow mapping, did not find expected ',' or '}'."); } } if (token is Key) { Skip(); token = GetCurrentToken(); if (!(token is Value) && !(token is FlowEntry) && !(token is FlowMappingEnd)) { states.Push(ParserState.FlowMappingValue); return ParseNode(isBlock: false, isIndentlessSequence: false); } state = ParserState.FlowMappingValue; return ProcessEmptyScalar(token?.Start ?? Mark.Empty); } if (token is YamlDotNet.Core.Tokens.Scalar) { states.Push(ParserState.FlowMappingValue); return ParseNode(isBlock: false, isIndentlessSequence: false); } if (!(token is FlowMappingEnd)) { states.Push(ParserState.FlowMappingEmptyValue); return ParseNode(isBlock: false, isIndentlessSequence: false); } } state = states.Pop(); Skip(); return new MappingEnd(token?.Start ?? Mark.Empty, token?.End ?? Mark.Empty); } private ParsingEvent ParseFlowMappingValue(bool isEmpty) { Token token = GetCurrentToken(); if (!isEmpty && token is Value) { Skip(); token = GetCurrentToken(); if (!(token is FlowEntry) && !(token is FlowMappingEnd)) { states.Push(ParserState.FlowMappingKey); return ParseNode(isBlock: false, isIndentlessSequence: false); } } state = ParserState.FlowMappingKey; if (!isEmpty && token is YamlDotNet.Core.Tokens.Scalar scalar) { Skip(); return new YamlDotNet.Core.Events.Scalar(AnchorName.Empty, TagName.Empty, scalar.Value, scalar.Style, isPlainImplicit: false, isQuotedImplicit: false, token.Start, scalar.End); } return ProcessEmptyScalar(token?.Start ?? Mark.Empty); } } internal static class ParserExtensions { public static T Consume(this IParser parser) where T : ParsingEvent { T result = parser.Require(); parser.MoveNext(); return result; } public static bool TryConsume(this IParser parser, [MaybeNullWhen(false)] out T @event) where T : ParsingEvent { if (parser.Accept(out @event)) { parser.MoveNext(); return true; } return false; } public static T Require(this IParser parser) where T : ParsingEvent { if (!parser.Accept(out var @event)) { ParsingEvent current = parser.Current; if (current == null) { throw new YamlException("Expected '" + typeof(T).Name + "', got nothing."); } throw new YamlException(current.Start, current.End, $"Expected '{typeof(T).Name}', got '{current.GetType().Name}' (at {current.Start})."); } return @event; } public static bool Accept(this IParser parser, [MaybeNullWhen(false)] out T @event) where T : ParsingEvent { if (parser.Current == null && !parser.MoveNext()) { throw new EndOfStreamException(); } if (parser.Current is T val) { @event = val; return true; } @event = null; return false; } public static void SkipThisAndNestedEvents(this IParser parser) { int num = 0; do { ParsingEvent parsingEvent = parser.Consume(); num += parsingEvent.NestingIncrease; } while (num > 0); } [Obsolete("Please use Consume() instead")] public static T Expect(this IParser parser) where T : ParsingEvent { return parser.Consume(); } [Obsolete("Please use TryConsume(out var evt) instead")] [return: MaybeNull] public static T? Allow(this IParser parser) where T : ParsingEvent { if (!parser.TryConsume(out var @event)) { return null; } return @event; } [Obsolete("Please use Accept(out var evt) instead")] [return: MaybeNull] public static T? Peek(this IParser parser) where T : ParsingEvent { if (!parser.Accept(out var @event)) { return null; } return @event; } [Obsolete("Please use TryConsume(out var evt) or Accept(out var evt) instead")] public static bool Accept(this IParser parser) where T : ParsingEvent { T @event; return parser.Accept(out @event); } public static bool TryFindMappingEntry(this IParser parser, Func selector, [MaybeNullWhen(false)] out YamlDotNet.Core.Events.Scalar? key, [MaybeNullWhen(false)] out ParsingEvent? value) { if (parser.TryConsume(out var _)) { while (parser.Current != null) { ParsingEvent current = parser.Current; if (!(current is YamlDotNet.Core.Events.Scalar scalar)) { if (current is MappingStart || current is SequenceStart) { parser.SkipThisAndNestedEvents(); } else { parser.MoveNext(); } continue; } bool flag = selector(scalar); parser.MoveNext(); if (flag) { value = parser.Current; key = scalar; return true; } parser.SkipThisAndNestedEvents(); } } key = null; value = null; return false; } } internal enum ParserState { StreamStart, StreamEnd, ImplicitDocumentStart, DocumentStart, DocumentContent, DocumentEnd, BlockNode, BlockNodeOrIndentlessSequence, FlowNode, BlockSequenceFirstEntry, BlockSequenceEntry, IndentlessSequenceEntry, BlockMappingFirstKey, BlockMappingKey, BlockMappingValue, FlowSequenceFirstEntry, FlowSequenceEntry, FlowSequenceEntryMappingKey, FlowSequenceEntryMappingValue, FlowSequenceEntryMappingEnd, FlowMappingFirstKey, FlowMappingKey, FlowMappingValue, FlowMappingEmptyValue } internal sealed class RecursionLevel { private int current; public int Maximum { get; } public RecursionLevel(int maximum) { Maximum = maximum; } public void Increment() { if (!TryIncrement()) { throw new MaximumRecursionLevelReachedException("Maximum level of recursion reached"); } } public bool TryIncrement() { if (current < Maximum) { current++; return true; } return false; } public void Decrement() { if (current == 0) { throw new InvalidOperationException("Attempted to decrement RecursionLevel to a negative value"); } current--; } } internal enum ScalarStyle { Any, Plain, SingleQuoted, DoubleQuoted, Literal, Folded, ForcePlain } internal class Scanner : IScanner { private const int MaxVersionNumberLength = 9; private static readonly SortedDictionary SimpleEscapeCodes = new SortedDictionary { { '0', '\0' }, { 'a', '\a' }, { 'b', '\b' }, { 't', '\t' }, { '\t', '\t' }, { 'n', '\n' }, { 'v', '\v' }, { 'f', '\f' }, { 'r', '\r' }, { 'e', '\u001b' }, { ' ', ' ' }, { '"', '"' }, { '\\', '\\' }, { '/', '/' }, { 'N', '\u0085' }, { '_', '\u00a0' }, { 'L', '\u2028' }, { 'P', '\u2029' } }; private readonly Stack indents = new Stack(); private readonly InsertionQueue tokens = new InsertionQueue(); private readonly Stack simpleKeys = new Stack(); private readonly CharacterAnalyzer analyzer; private readonly Cursor cursor; private bool streamStartProduced; private bool streamEndProduced; private bool plainScalarFollowedByComment; private bool flowCollectionFetched; private bool startFlowCollectionFetched; private long indent = -1L; private bool flowScalarFetched; private bool simpleKeyAllowed; private int flowLevel; private int tokensParsed; private bool tokenAvailable; private Token? previous; private Anchor? previousAnchor; private YamlDotNet.Core.Tokens.Scalar? lastScalar; private readonly int maxKeySize; private static readonly byte[] EmptyBytes = Array.Empty(); public bool SkipComments { get; private set; } public Token? Current { get; private set; } public Mark CurrentPosition => cursor.Mark(); private bool IsDocumentStart() { if (!analyzer.EndOfInput && cursor.LineOffset == 0L && analyzer.Check('-') && analyzer.Check('-', 1) && analyzer.Check('-', 2)) { return analyzer.IsWhiteBreakOrZero(3); } return false; } private bool IsDocumentEnd() { if (!analyzer.EndOfInput && cursor.LineOffset == 0L && analyzer.Check('.') && analyzer.Check('.', 1) && analyzer.Check('.', 2)) { return analyzer.IsWhiteBreakOrZero(3); } return false; } private bool IsDocumentIndicator() { if (!IsDocumentStart()) { return IsDocumentEnd(); } return true; } public Scanner(TextReader input, bool skipComments = true) : this(input, skipComments, 1024) { } public Scanner(TextReader input, bool skipComments, int maxKeySize) { analyzer = new CharacterAnalyzer(new LookAheadBuffer(input, 1024)); cursor = new Cursor(); SkipComments = skipComments; this.maxKeySize = maxKeySize; } public bool MoveNext() { if (Current != null) { ConsumeCurrent(); } return MoveNextWithoutConsuming(); } public bool MoveNextWithoutConsuming() { if (!tokenAvailable && !streamEndProduced) { FetchMoreTokens(); } if (tokens.Count > 0) { Current = tokens.Dequeue(); tokenAvailable = false; return true; } Current = null; return false; } public void ConsumeCurrent() { tokensParsed++; tokenAvailable = false; previous = Current; Current = null; } private char ReadCurrentCharacter() { char result = analyzer.Peek(0); Skip(); return result; } private char ReadLine() { if (analyzer.Check("\r\n\u0085")) { SkipLine(); return '\n'; } char result = analyzer.Peek(0); SkipLine(); return result; } private void FetchMoreTokens() { while (true) { bool flag = false; if (tokens.Count == 0) { flag = true; } else { foreach (SimpleKey simpleKey in simpleKeys) { if (simpleKey.IsPossible && simpleKey.TokenNumber == tokensParsed) { flag = true; break; } } } if (!flag) { break; } FetchNextToken(); } tokenAvailable = true; } private static bool StartsWith(StringBuilder what, char start) { if (what.Length > 0) { return what[0] == start; } return false; } private void StaleSimpleKeys() { foreach (SimpleKey simpleKey in simpleKeys) { if (simpleKey.IsPossible && (simpleKey.Line < cursor.Line || simpleKey.Index + maxKeySize < cursor.Index)) { if (simpleKey.IsRequired) { Mark mark = cursor.Mark(); tokens.Enqueue(new Error("While scanning a simple key, could not find expected ':'.", mark, mark)); } simpleKey.MarkAsImpossible(); } } } private void FetchNextToken() { if (!streamStartProduced) { FetchStreamStart(); return; } ScanToNextToken(); StaleSimpleKeys(); UnrollIndent(cursor.LineOffset); analyzer.Buffer.Cache(4); if (analyzer.Buffer.EndOfInput) { lastScalar = null; FetchStreamEnd(); } if (cursor.LineOffset == 0L && analyzer.Check('%')) { lastScalar = null; FetchDirective(); return; } if (IsDocumentStart()) { lastScalar = null; FetchDocumentIndicator(isStartToken: true); return; } if (IsDocumentEnd()) { lastScalar = null; FetchDocumentIndicator(isStartToken: false); return; } if (analyzer.Check('[')) { lastScalar = null; FetchFlowCollectionStart(isSequenceToken: true); return; } if (analyzer.Check('{')) { lastScalar = null; FetchFlowCollectionStart(isSequenceToken: false); return; } if (analyzer.Check(']')) { lastScalar = null; FetchFlowCollectionEnd(isSequenceToken: true); return; } if (analyzer.Check('}')) { lastScalar = null; FetchFlowCollectionEnd(isSequenceToken: false); return; } if (analyzer.Check(',')) { lastScalar = null; FetchFlowEntry(); return; } if (analyzer.Check('-')) { if (analyzer.IsWhiteBreakOrZero(1)) { FetchBlockEntry(); return; } if (flowLevel > 0 && analyzer.Check(",[]{}", 1)) { tokens.Enqueue(new Error("Invalid key indicator format.", cursor.Mark(), cursor.Mark())); } } if (analyzer.Check('?') && (flowLevel > 0 || analyzer.IsWhiteBreakOrZero(1)) && analyzer.IsWhiteBreakOrZero(1)) { FetchKey(); } else if (analyzer.Check(':') && (flowLevel > 0 || analyzer.IsWhiteBreakOrZero(1)) && (!simpleKeyAllowed || flowLevel <= 0) && (!flowScalarFetched || !analyzer.Check(':', 1)) && (analyzer.IsWhiteBreakOrZero(1) || analyzer.Check(',', 1) || flowScalarFetched || flowCollectionFetched || startFlowCollectionFetched)) { if (lastScalar != null) { lastScalar.IsKey = true; lastScalar = null; } FetchValue(); } else if (analyzer.Check('*')) { FetchAnchor(isAlias: true); } else if (analyzer.Check('&')) { FetchAnchor(isAlias: false); } else if (analyzer.Check('!')) { FetchTag(); } else if (analyzer.Check('|') && flowLevel == 0) { FetchBlockScalar(isLiteral: true); } else if (analyzer.Check('>') && flowLevel == 0) { FetchBlockScalar(isLiteral: false); } else if (analyzer.Check('\'')) { FetchQuotedScalar(isSingleQuoted: true); } else if (analyzer.Check('"')) { FetchQuotedScalar(isSingleQuoted: false); } else if ((!analyzer.IsWhiteBreakOrZero() && !analyzer.Check("-?:,[]{}#&*!|>'\"%@`")) || (analyzer.Check('-') && !analyzer.IsWhite(1)) || (analyzer.Check("?:") && !analyzer.IsWhiteBreakOrZero(1)) || (simpleKeyAllowed && flowLevel > 0)) { if (plainScalarFollowedByComment) { Mark mark = cursor.Mark(); tokens.Enqueue(new Error("While scanning plain scalar, found a comment between adjacent scalars.", mark, mark)); } if ((flowScalarFetched || (flowCollectionFetched && !startFlowCollectionFetched)) && analyzer.Check(':')) { Skip(); } flowScalarFetched = false; flowCollectionFetched = false; startFlowCollectionFetched = false; plainScalarFollowedByComment = false; FetchPlainScalar(); } else { if (simpleKeyAllowed && indent >= cursor.LineOffset && analyzer.IsTab()) { throw new SyntaxErrorException("While scanning a mapping, found invalid tab as indentation."); } if (!analyzer.IsWhiteBreakOrZero()) { Mark start = cursor.Mark(); Skip(); throw new SyntaxErrorException(in start, cursor.Mark(), "While scanning for the next token, found character that cannot start any token."); } Skip(); } } private bool CheckWhiteSpace() { if (!analyzer.Check(' ')) { if (flowLevel > 0 || !simpleKeyAllowed) { return analyzer.Check('\t'); } return false; } return true; } private void Skip() { cursor.Skip(); analyzer.Buffer.Skip(1); } private void SkipLine() { if (analyzer.IsCrLf()) { cursor.SkipLineByOffset(2); analyzer.Buffer.Skip(2); } else if (analyzer.IsBreak()) { cursor.SkipLineByOffset(1); analyzer.Buffer.Skip(1); } else if (!analyzer.IsZero()) { throw new InvalidOperationException("Not at a break."); } } private void ScanToNextToken() { while (true) { if (CheckWhiteSpace()) { Skip(); continue; } ProcessComment(); if (analyzer.IsBreak()) { SkipLine(); if (flowLevel == 0) { simpleKeyAllowed = true; } continue; } break; } } private void ProcessComment() { if (!analyzer.Check('#')) { return; } Mark start = cursor.Mark(); Skip(); while (analyzer.IsSpace()) { Skip(); } StringBuilderPool.BuilderWrapper builderWrapper = StringBuilderPool.Rent(); try { StringBuilder builder = builderWrapper.Builder; while (!analyzer.IsBreakOrZero()) { builder.Append(ReadCurrentCharacter()); } if (!SkipComments) { bool isInline = previous != null && previous.End.Line == start.Line && previous.End.Column != 1 && !(previous is YamlDotNet.Core.Tokens.StreamStart); tokens.Enqueue(new YamlDotNet.Core.Tokens.Comment(builder.ToString(), isInline, start, cursor.Mark())); } } finally { ((IDisposable)builderWrapper/*cast due to .constrained prefix*/).Dispose(); } } private void FetchStreamStart() { simpleKeys.Push(new SimpleKey()); simpleKeyAllowed = true; streamStartProduced = true; Mark start = cursor.Mark(); tokens.Enqueue(new YamlDotNet.Core.Tokens.StreamStart(in start, in start)); } private void UnrollIndent(long column) { if (flowLevel == 0) { while (indent > column) { Mark start = cursor.Mark(); tokens.Enqueue(new BlockEnd(in start, in start)); indent = indents.Pop(); } } } private void FetchStreamEnd() { cursor.ForceSkipLineAfterNonBreak(); UnrollIndent(-1L); RemoveSimpleKey(); simpleKeyAllowed = false; streamEndProduced = true; Mark start = cursor.Mark(); tokens.Enqueue(new YamlDotNet.Core.Tokens.StreamEnd(in start, in start)); } private void FetchDirective() { UnrollIndent(-1L); RemoveSimpleKey(); simpleKeyAllowed = false; Token token = ScanDirective(); if (token != null) { tokens.Enqueue(token); } } private Token? ScanDirective() { Mark start = cursor.Mark(); Skip(); string text = ScanDirectiveName(in start); Token result; if (!(text == "YAML")) { if (!(text == "TAG")) { while (!analyzer.EndOfInput && !analyzer.Check('#') && !analyzer.IsBreak()) { Skip(); } return null; } result = ScanTagDirectiveValue(in start); } else { if (!(previous is YamlDotNet.Core.Tokens.DocumentStart) && !(previous is YamlDotNet.Core.Tokens.StreamStart) && !(previous is YamlDotNet.Core.Tokens.DocumentEnd)) { throw new SemanticErrorException(in start, cursor.Mark(), "While scanning a version directive, did not find preceding ."); } result = ScanVersionDirectiveValue(in start); } while (analyzer.IsWhite()) { Skip(); } ProcessComment(); if (!analyzer.IsBreakOrZero()) { throw new SyntaxErrorException(in start, cursor.Mark(), "While scanning a directive, did not find expected comment or line break."); } if (analyzer.IsBreak()) { SkipLine(); } return result; } private void FetchDocumentIndicator(bool isStartToken) { UnrollIndent(-1L); RemoveSimpleKey(); simpleKeyAllowed = false; Mark start = cursor.Mark(); Skip(); Skip(); Skip(); if (isStartToken) { tokens.Enqueue(new YamlDotNet.Core.Tokens.DocumentStart(in start, cursor.Mark())); return; } Token token = null; while (!analyzer.EndOfInput && !analyzer.IsBreak() && !analyzer.Check('#')) { if (!analyzer.IsWhite()) { token = new Error("While scanning a document end, found invalid content after '...' marker.", start, cursor.Mark()); break; } Skip(); } tokens.Enqueue(new YamlDotNet.Core.Tokens.DocumentEnd(in start, in start)); if (token != null) { tokens.Enqueue(token); } } private void FetchFlowCollectionStart(bool isSequenceToken) { SaveSimpleKey(); IncreaseFlowLevel(); simpleKeyAllowed = true; Mark start = cursor.Mark(); Skip(); Token item = ((!isSequenceToken) ? ((Token)new FlowMappingStart(in start, in start)) : ((Token)new FlowSequenceStart(in start, in start))); tokens.Enqueue(item); startFlowCollectionFetched = true; } private void IncreaseFlowLevel() { simpleKeys.Push(new SimpleKey()); flowLevel++; } private void FetchFlowCollectionEnd(bool isSequenceToken) { RemoveSimpleKey(); DecreaseFlowLevel(); simpleKeyAllowed = false; Mark start = cursor.Mark(); Skip(); Token token = null; Token item; if (isSequenceToken) { if (analyzer.Check('#')) { token = new Error("While scanning a flow sequence end, found invalid comment after ']'.", start, start); } item = new FlowSequenceEnd(in start, in start); } else { item = new FlowMappingEnd(in start, in start); } tokens.Enqueue(item); if (token != null) { tokens.Enqueue(token); } flowCollectionFetched = true; } private void DecreaseFlowLevel() { if (flowLevel > 0) { flowLevel--; simpleKeys.Pop(); } } private void FetchFlowEntry() { RemoveSimpleKey(); simpleKeyAllowed = true; Mark start = cursor.Mark(); Skip(); Mark end = cursor.Mark(); if (analyzer.Check('#')) { tokens.Enqueue(new Error("While scanning a flow entry, found invalid comment after comma.", start, end)); } else { tokens.Enqueue(new FlowEntry(in start, in end)); } } private void FetchBlockEntry() { if (flowLevel == 0) { if (!simpleKeyAllowed) { if (previousAnchor != null && previousAnchor.End.Line == cursor.Line) { throw new SemanticErrorException(previousAnchor.Start, previousAnchor.End, "Anchor before sequence entry on same line is not allowed."); } Mark mark = cursor.Mark(); tokens.Enqueue(new Error("Block sequence entries are not allowed in this context.", mark, mark)); } RollIndent(cursor.LineOffset, -1, isSequence: true, cursor.Mark()); } RemoveSimpleKey(); simpleKeyAllowed = true; Mark start = cursor.Mark(); Skip(); tokens.Enqueue(new BlockEntry(in start, cursor.Mark())); } private void FetchKey() { if (flowLevel == 0) { if (!simpleKeyAllowed) { Mark start = cursor.Mark(); throw new SyntaxErrorException(in start, in start, "Mapping keys are not allowed in this context."); } RollIndent(cursor.LineOffset, -1, isSequence: false, cursor.Mark()); } RemoveSimpleKey(); simpleKeyAllowed = flowLevel == 0; Mark start2 = cursor.Mark(); Skip(); tokens.Enqueue(new Key(in start2, cursor.Mark())); } private void FetchValue() { SimpleKey simpleKey = simpleKeys.Peek(); if (simpleKey.IsPossible) { tokens.Insert(simpleKey.TokenNumber - tokensParsed, new Key(simpleKey.Mark, simpleKey.Mark)); RollIndent(simpleKey.LineOffset, simpleKey.TokenNumber, isSequence: false, simpleKey.Mark); simpleKey.MarkAsImpossible(); simpleKeyAllowed = false; } else { bool flag = flowLevel == 0; if (flag) { if (!simpleKeyAllowed) { Mark mark = cursor.Mark(); tokens.Enqueue(new Error("Mapping values are not allowed in this context.", mark, mark)); return; } RollIndent(cursor.LineOffset, -1, isSequence: false, cursor.Mark()); if (cursor.LineOffset == 0L && simpleKey.LineOffset == 0L) { tokens.Insert(tokens.Count, new Key(simpleKey.Mark, simpleKey.Mark)); flag = false; } } simpleKeyAllowed = flag; } Mark start = cursor.Mark(); Skip(); tokens.Enqueue(new Value(in start, cursor.Mark())); } private void RollIndent(long column, int number, bool isSequence, Mark position) { if (flowLevel <= 0 && indent < column) { indents.Push(indent); indent = column; Token item = ((!isSequence) ? ((Token)new BlockMappingStart(in position, in position)) : ((Token)new BlockSequenceStart(in position, in position))); if (number == -1) { tokens.Enqueue(item); } else { tokens.Insert(number - tokensParsed, item); } } } private void FetchAnchor(bool isAlias) { SaveSimpleKey(); simpleKeyAllowed = false; tokens.Enqueue(ScanAnchor(isAlias)); } private Token ScanAnchor(bool isAlias) { Mark start = cursor.Mark(); Skip(); bool flag = false; if (isAlias) { SimpleKey simpleKey = simpleKeys.Peek(); flag = simpleKey.IsRequired && simpleKey.IsPossible; } StringBuilderPool.BuilderWrapper builderWrapper = StringBuilderPool.Rent(); try { StringBuilder builder = builderWrapper.Builder; while (!analyzer.IsWhiteBreakOrZero() && !analyzer.Check("[]{},") && (!flag || !analyzer.Check(':') || !analyzer.IsWhiteBreakOrZero(1))) { builder.Append(ReadCurrentCharacter()); } if (builder.Length == 0 || (!analyzer.IsWhiteBreakOrZero() && !analyzer.Check("?:,]}%@`"))) { throw new SyntaxErrorException(in start, cursor.Mark(), "While scanning an anchor or alias, found value containing disallowed: []{},"); } AnchorName value = new AnchorName(builder.ToString()); if (isAlias) { return new YamlDotNet.Core.Tokens.AnchorAlias(value, start, cursor.Mark()); } return previousAnchor = new Anchor(value, start, cursor.Mark()); } finally { ((IDisposable)builderWrapper/*cast due to .constrained prefix*/).Dispose(); } } private void FetchTag() { SaveSimpleKey(); simpleKeyAllowed = false; tokens.Enqueue(ScanTag()); } private Tag ScanTag() { Mark start = cursor.Mark(); string text; string text2; if (analyzer.Check('<', 1)) { text = string.Empty; Skip(); Skip(); text2 = ScanTagUri(null, start); if (!analyzer.Check('>')) { throw new SyntaxErrorException(in start, cursor.Mark(), "While scanning a tag, did not find the expected '>'."); } Skip(); } else { string text3 = ScanTagHandle(isDirective: false, start); if (text3.Length > 1 && text3[0] == '!' && text3[text3.Length - 1] == '!') { text = text3; text2 = ScanTagUri(null, start); } else { text2 = ScanTagUri(text3, start); text = "!"; if (text2.Length == 0) { text2 = text; text = string.Empty; } } } if (!analyzer.IsWhiteBreakOrZero() && !analyzer.Check(',')) { throw new SyntaxErrorException(in start, cursor.Mark(), "While scanning a tag, did not find expected whitespace, comma or line break."); } return new Tag(text, text2, start, cursor.Mark()); } private void FetchBlockScalar(bool isLiteral) { SaveSimpleKey(); simpleKeyAllowed = true; tokens.Enqueue(ScanBlockScalar(isLiteral)); } private YamlDotNet.Core.Tokens.Scalar ScanBlockScalar(bool isLiteral) { StringBuilderPool.BuilderWrapper builderWrapper = StringBuilderPool.Rent(); try { StringBuilder builder = builderWrapper.Builder; StringBuilderPool.BuilderWrapper builderWrapper2 = StringBuilderPool.Rent(); try { StringBuilder builder2 = builderWrapper2.Builder; StringBuilderPool.BuilderWrapper builderWrapper3 = StringBuilderPool.Rent(); try { StringBuilder builder3 = builderWrapper3.Builder; int num = 0; int num2 = 0; long currentIndent = 0L; bool flag = false; bool? isFirstLine = null; Mark start = cursor.Mark(); Skip(); if (analyzer.Check("+-")) { num = (analyzer.Check('+') ? 1 : (-1)); Skip(); if (analyzer.IsDigit()) { if (analyzer.Check('0')) { throw new SyntaxErrorException(in start, cursor.Mark(), "While scanning a block scalar, found an indentation indicator equal to 0."); } num2 = analyzer.AsDigit(); Skip(); } } else if (analyzer.IsDigit()) { if (analyzer.Check('0')) { throw new SyntaxErrorException(in start, cursor.Mark(), "While scanning a block scalar, found an indentation indicator equal to 0."); } num2 = analyzer.AsDigit(); Skip(); if (analyzer.Check("+-")) { num = (analyzer.Check('+') ? 1 : (-1)); Skip(); } } if (analyzer.Check('#')) { throw new SyntaxErrorException(in start, cursor.Mark(), "While scanning a block scalar, found a comment without whtespace after '>' indicator."); } while (analyzer.IsWhite()) { Skip(); } ProcessComment(); if (!analyzer.IsBreakOrZero()) { throw new SyntaxErrorException(in start, cursor.Mark(), "While scanning a block scalar, did not find expected comment or line break."); } if (analyzer.IsBreak()) { SkipLine(); if (!isFirstLine.HasValue) { isFirstLine = true; } else if (isFirstLine == true) { isFirstLine = false; } } Mark end = cursor.Mark(); if (num2 != 0) { currentIndent = ((indent >= 0) ? (indent + num2) : num2); } currentIndent = ScanBlockScalarBreaks(currentIndent, builder3, isLiteral, ref end, ref isFirstLine); isFirstLine = false; while (cursor.LineOffset == currentIndent && !analyzer.IsZero() && !IsDocumentEnd()) { bool flag2 = analyzer.IsWhite(); if (!isLiteral && StartsWith(builder2, '\n') && !flag && !flag2) { if (builder3.Length == 0) { builder.Append(' '); } builder2.Length = 0; } else { builder.Append((object?)builder2); builder2.Length = 0; } builder.Append((object?)builder3); builder3.Length = 0; flag = analyzer.IsWhite(); while (!analyzer.IsBreakOrZero()) { builder.Append(ReadCurrentCharacter()); } char c = ReadLine(); if (c != 0) { builder2.Append(c); } currentIndent = ScanBlockScalarBreaks(currentIndent, builder3, isLiteral, ref end, ref isFirstLine); } if (num != -1) { builder.Append((object?)builder2); } if (num == 1) { builder.Append((object?)builder3); } ScalarStyle style = (isLiteral ? ScalarStyle.Literal : ScalarStyle.Folded); return new YamlDotNet.Core.Tokens.Scalar(builder.ToString(), style, start, end); } finally { ((IDisposable)builderWrapper3/*cast due to .constrained prefix*/).Dispose(); } } finally { ((IDisposable)builderWrapper2/*cast due to .constrained prefix*/).Dispose(); } } finally { ((IDisposable)builderWrapper/*cast due to .constrained prefix*/).Dispose(); } } private long ScanBlockScalarBreaks(long currentIndent, StringBuilder breaks, bool isLiteral, ref Mark end, ref bool? isFirstLine) { long num = 0L; long num2 = -1L; end = cursor.Mark(); while (true) { if ((currentIndent == 0L || cursor.LineOffset < currentIndent) && analyzer.IsSpace()) { Skip(); continue; } if (cursor.LineOffset > num) { num = cursor.LineOffset; } if (!analyzer.IsBreak()) { break; } if (isFirstLine == true) { isFirstLine = false; num2 = cursor.LineOffset; } breaks.Append(ReadLine()); end = cursor.Mark(); } if (isLiteral && isFirstLine == true) { long num3 = cursor.LineOffset; int num4 = 0; while (!analyzer.IsBreak(num4) && analyzer.IsSpace(num4)) { num4++; num3++; } if (analyzer.IsBreak(num4) && num3 > cursor.LineOffset) { isFirstLine = false; num2 = num3; } } if (isLiteral && num2 > 1 && currentIndent < num2 - 1) { throw new SemanticErrorException(in end, cursor.Mark(), "While scanning a literal block scalar, found extra spaces in first line."); } if (!isLiteral && num > cursor.LineOffset && num2 > -1) { throw new SemanticErrorException(in end, cursor.Mark(), "While scanning a literal block scalar, found more spaces in lines above first content line."); } if (currentIndent == 0L && (cursor.LineOffset > 0 || indent > -1)) { currentIndent = Math.Max(num, Math.Max(indent + 1, 1L)); } return currentIndent; } private void FetchQuotedScalar(bool isSingleQuoted) { SaveSimpleKey(); simpleKeyAllowed = false; flowScalarFetched = flowLevel > 0; YamlDotNet.Core.Tokens.Scalar item = ScanFlowScalar(isSingleQuoted); tokens.Enqueue(item); lastScalar = item; if (!isSingleQuoted && analyzer.Check('#')) { Mark mark = cursor.Mark(); tokens.Enqueue(new Error("While scanning a flow sequence end, found invalid comment after double-quoted scalar.", mark, mark)); } } private YamlDotNet.Core.Tokens.Scalar ScanFlowScalar(bool isSingleQuoted) { Mark start = cursor.Mark(); Skip(); StringBuilderPool.BuilderWrapper builderWrapper = StringBuilderPool.Rent(); try { StringBuilder builder = builderWrapper.Builder; StringBuilderPool.BuilderWrapper builderWrapper2 = StringBuilderPool.Rent(); try { StringBuilder builder2 = builderWrapper2.Builder; StringBuilderPool.BuilderWrapper builderWrapper3 = StringBuilderPool.Rent(); try { StringBuilder builder3 = builderWrapper3.Builder; StringBuilderPool.BuilderWrapper builderWrapper4 = StringBuilderPool.Rent(); try { StringBuilder builder4 = builderWrapper4.Builder; bool flag = false; while (true) { if (IsDocumentIndicator()) { throw new SyntaxErrorException(in start, cursor.Mark(), "While scanning a quoted scalar, found unexpected document indicator."); } if (analyzer.IsZero()) { throw new SyntaxErrorException(in start, cursor.Mark(), "While scanning a quoted scalar, found unexpected end of stream."); } if (flag && !isSingleQuoted && indent >= cursor.LineOffset) { throw new SyntaxErrorException(in start, cursor.Mark(), "While scanning a multi-line double-quoted scalar, found wrong indentation."); } flag = false; while (!analyzer.IsWhiteBreakOrZero()) { if (isSingleQuoted && analyzer.Check('\'') && analyzer.Check('\'', 1)) { builder.Append('\''); Skip(); Skip(); continue; } if (analyzer.Check(isSingleQuoted ? '\'' : '"')) { break; } if (!isSingleQuoted && analyzer.Check('\\') && analyzer.IsBreak(1)) { Skip(); SkipLine(); flag = true; break; } if (!isSingleQuoted && analyzer.Check('\\')) { int num = 0; char c = analyzer.Peek(1); switch (c) { case 'x': num = 2; break; case 'u': num = 4; break; case 'U': num = 8; break; default: { if (SimpleEscapeCodes.TryGetValue(c, out var value)) { builder.Append(value); break; } throw new SyntaxErrorException(in start, cursor.Mark(), "While scanning a quoted scalar, found unknown escape character."); } } Skip(); Skip(); if (num <= 0) { continue; } int num2 = 0; for (int i = 0; i < num; i++) { if (!analyzer.IsHex(i)) { throw new SyntaxErrorException(in start, cursor.Mark(), "While scanning a quoted scalar, did not find expected hexadecimal number."); } num2 = (num2 << 4) + analyzer.AsHex(i); } if (num2 >= 55296 && num2 <= 57343) { for (int j = 0; j < num; j++) { Skip(); } if (analyzer.Peek(0) != '\\' || (analyzer.Peek(1) != 'u' && analyzer.Peek(1) != 'U')) { throw new SyntaxErrorException(in start, cursor.Mark(), "While scanning a quoted scalar, found invalid Unicode surrogates."); } Skip(); num = ((analyzer.Peek(0) != 'u') ? 8 : 4); Skip(); int num3 = 0; for (int k = 0; k < num; k++) { if (!analyzer.IsHex(0)) { throw new SyntaxErrorException(in start, cursor.Mark(), "While scanning a quoted scalar, did not find expected hexadecimal number."); } num3 = (num3 << 4) + analyzer.AsHex(k); } for (int l = 0; l < num; l++) { Skip(); } num2 = char.ConvertToUtf32((char)num2, (char)num3); } else { if (num2 > 1114111) { throw new SyntaxErrorException(in start, cursor.Mark(), "While scanning a quoted scalar, found invalid Unicode character escape code."); } for (int m = 0; m < num; m++) { Skip(); } } builder.Append(char.ConvertFromUtf32(num2)); } else { builder.Append(ReadCurrentCharacter()); } } if (analyzer.Check(isSingleQuoted ? '\'' : '"')) { break; } while (analyzer.IsWhite() || analyzer.IsBreak()) { if (analyzer.IsWhite()) { if (!flag) { builder2.Append(ReadCurrentCharacter()); } else { Skip(); } } else if (!flag) { builder2.Length = 0; builder3.Append(ReadLine()); flag = true; } else { builder4.Append(ReadLine()); } } if (flag) { if (StartsWith(builder3, '\n')) { if (builder4.Length == 0) { builder.Append(' '); } else { builder.Append((object?)builder4); } } else { builder.Append((object?)builder3); builder.Append((object?)builder4); } builder3.Length = 0; builder4.Length = 0; } else { builder.Append((object?)builder2); builder2.Length = 0; } } Skip(); return new YamlDotNet.Core.Tokens.Scalar(builder.ToString(), isSingleQuoted ? ScalarStyle.SingleQuoted : ScalarStyle.DoubleQuoted, start, cursor.Mark()); } finally { ((IDisposable)builderWrapper4/*cast due to .constrained prefix*/).Dispose(); } } finally { ((IDisposable)builderWrapper3/*cast due to .constrained prefix*/).Dispose(); } } finally { ((IDisposable)builderWrapper2/*cast due to .constrained prefix*/).Dispose(); } } finally { ((IDisposable)builderWrapper/*cast due to .constrained prefix*/).Dispose(); } } private void FetchPlainScalar() { SaveSimpleKey(); simpleKeyAllowed = false; bool isMultiline = false; YamlDotNet.Core.Tokens.Scalar item = (lastScalar = ScanPlainScalar(ref isMultiline)); if (isMultiline && analyzer.Check(':') && flowLevel == 0 && indent < cursor.LineOffset) { tokens.Enqueue(new Error("While scanning a multiline plain scalar, found invalid mapping.", cursor.Mark(), cursor.Mark())); } tokens.Enqueue(item); } private YamlDotNet.Core.Tokens.Scalar ScanPlainScalar(ref bool isMultiline) { StringBuilderPool.BuilderWrapper builderWrapper = StringBuilderPool.Rent(); try { StringBuilder builder = builderWrapper.Builder; StringBuilderPool.BuilderWrapper builderWrapper2 = StringBuilderPool.Rent(); try { StringBuilder builder2 = builderWrapper2.Builder; StringBuilderPool.BuilderWrapper builderWrapper3 = StringBuilderPool.Rent(); try { StringBuilder builder3 = builderWrapper3.Builder; StringBuilderPool.BuilderWrapper builderWrapper4 = StringBuilderPool.Rent(); try { StringBuilder builder4 = builderWrapper4.Builder; bool flag = false; long num = indent + 1; Mark start = cursor.Mark(); Mark end = start; SimpleKey simpleKey = simpleKeys.Peek(); while (!IsDocumentIndicator()) { if (analyzer.Check('#')) { if (indent < 0 && flowLevel == 0) { plainScalarFollowedByComment = true; } break; } bool flag2 = analyzer.Check('*') && (!simpleKey.IsPossible || !simpleKey.IsRequired); while (!analyzer.IsWhiteBreakOrZero()) { if ((analyzer.Check(':') && !flag2 && (analyzer.IsWhiteBreakOrZero(1) || (flowLevel > 0 && analyzer.Check(',', 1)))) || (flowLevel > 0 && analyzer.Check(",[]{}"))) { if (flowLevel == 0 && !simpleKey.IsPossible) { tokens.Enqueue(new Error("While scanning a plain scalar value, found invalid mapping.", cursor.Mark(), cursor.Mark())); } break; } if (flag || builder2.Length > 0) { if (flag) { if (StartsWith(builder3, '\n')) { if (builder4.Length == 0) { builder.Append(' '); } else { builder.Append((object?)builder4); } } else { builder.Append((object?)builder3); builder.Append((object?)builder4); } builder3.Length = 0; builder4.Length = 0; flag = false; } else { builder.Append((object?)builder2); builder2.Length = 0; } } if (flowLevel > 0 && cursor.LineOffset < num) { throw new InvalidOperationException(); } builder.Append(ReadCurrentCharacter()); end = cursor.Mark(); } if (!analyzer.IsWhite() && !analyzer.IsBreak()) { break; } while (analyzer.IsWhite() || analyzer.IsBreak()) { if (analyzer.IsWhite()) { if (flag && cursor.LineOffset < num && analyzer.IsTab()) { throw new SyntaxErrorException(in start, cursor.Mark(), "While scanning a plain scalar, found a tab character that violate indentation."); } if (!flag) { builder2.Append(ReadCurrentCharacter()); } else { Skip(); } } else { isMultiline = true; if (!flag) { builder2.Length = 0; builder3.Append(ReadLine()); flag = true; } else { builder4.Append(ReadLine()); } } } if (flowLevel == 0 && cursor.LineOffset < num) { break; } } if (flag) { simpleKeyAllowed = true; } return new YamlDotNet.Core.Tokens.Scalar(builder.ToString(), ScalarStyle.Plain, start, end); } finally { ((IDisposable)builderWrapper4/*cast due to .constrained prefix*/).Dispose(); } } finally { ((IDisposable)builderWrapper3/*cast due to .constrained prefix*/).Dispose(); } } finally { ((IDisposable)builderWrapper2/*cast due to .constrained prefix*/).Dispose(); } } finally { ((IDisposable)builderWrapper/*cast due to .constrained prefix*/).Dispose(); } } private void RemoveSimpleKey() { SimpleKey simpleKey = simpleKeys.Peek(); if (simpleKey.IsPossible && simpleKey.IsRequired) { throw new SyntaxErrorException(simpleKey.Mark, simpleKey.Mark, "While scanning a simple key, could not find expected ':'."); } simpleKey.MarkAsImpossible(); } private string ScanDirectiveName(in Mark start) { StringBuilderPool.BuilderWrapper builderWrapper = StringBuilderPool.Rent(); try { StringBuilder builder = builderWrapper.Builder; while (analyzer.IsAlphaNumericDashOrUnderscore()) { builder.Append(ReadCurrentCharacter()); } if (builder.Length == 0) { throw new SyntaxErrorException(in start, cursor.Mark(), "While scanning a directive, could not find expected directive name."); } if (analyzer.EndOfInput) { throw new SyntaxErrorException(in start, cursor.Mark(), "While scanning a directive, found unexpected end of stream."); } if (!analyzer.IsWhiteBreakOrZero()) { throw new SyntaxErrorException(in start, cursor.Mark(), "While scanning a directive, found unexpected non-alphabetical character."); } return builder.ToString(); } finally { ((IDisposable)builderWrapper/*cast due to .constrained prefix*/).Dispose(); } } private void SkipWhitespaces() { while (analyzer.IsWhite()) { Skip(); } } private VersionDirective ScanVersionDirectiveValue(in Mark start) { SkipWhitespaces(); int major = ScanVersionDirectiveNumber(in start); if (!analyzer.Check('.')) { throw new SyntaxErrorException(in start, cursor.Mark(), "While scanning a %YAML directive, did not find expected digit or '.' character."); } Skip(); int minor = ScanVersionDirectiveNumber(in start); return new VersionDirective(new Version(major, minor), start, start); } private TagDirective ScanTagDirectiveValue(in Mark start) { SkipWhitespaces(); string handle = ScanTagHandle(isDirective: true, start); if (!analyzer.IsWhite()) { throw new SyntaxErrorException(in start, cursor.Mark(), "While scanning a %TAG directive, did not find expected whitespace."); } SkipWhitespaces(); string prefix = ScanTagUri(null, start); if (!analyzer.IsWhiteBreakOrZero()) { throw new SyntaxErrorException(in start, cursor.Mark(), "While scanning a %TAG directive, did not find expected whitespace or line break."); } return new TagDirective(handle, prefix, start, start); } private string ScanTagUri(string? head, Mark start) { StringBuilderPool.BuilderWrapper builderWrapper = StringBuilderPool.Rent(); try { StringBuilder builder = builderWrapper.Builder; if (head != null && head.Length > 1) { builder.Append(head.Substring(1)); } while (analyzer.IsAlphaNumericDashOrUnderscore() || analyzer.Check(";/?:@&=+$.!~*'()[]%") || (analyzer.Check(',') && !analyzer.IsBreak(1))) { if (analyzer.Check('%')) { builder.Append(ScanUriEscapes(in start)); } else if (analyzer.Check('+')) { builder.Append(' '); Skip(); } else { builder.Append(ReadCurrentCharacter()); } } if (builder.Length == 0) { return string.Empty; } string text = builder.ToString(); if (Polyfills.EndsWith(text, ',')) { throw new SyntaxErrorException(cursor.Mark(), cursor.Mark(), "Unexpected comma at end of tag"); } return text; } finally { ((IDisposable)builderWrapper/*cast due to .constrained prefix*/).Dispose(); } } private string ScanUriEscapes(in Mark start) { byte[] array = EmptyBytes; int count = 0; int num = 0; do { if (!analyzer.Check('%') || !analyzer.IsHex(1) || !analyzer.IsHex(2)) { throw new SyntaxErrorException(in start, cursor.Mark(), "While scanning a tag, did not find URI escaped octet."); } int num2 = (analyzer.AsHex(1) << 4) + analyzer.AsHex(2); if (num == 0) { num = (((num2 & 0x80) == 0) ? 1 : (((num2 & 0xE0) == 192) ? 2 : (((num2 & 0xF0) == 224) ? 3 : (((num2 & 0xF8) == 240) ? 4 : 0)))); if (num == 0) { throw new SyntaxErrorException(in start, cursor.Mark(), "While scanning a tag, found an incorrect leading UTF-8 octet."); } array = new byte[num]; } else if ((num2 & 0xC0) != 128) { throw new SyntaxErrorException(in start, cursor.Mark(), "While scanning a tag, found an incorrect trailing UTF-8 octet."); } array[count++] = (byte)num2; Skip(); Skip(); Skip(); } while (--num > 0); string text = Encoding.UTF8.GetString(array, 0, count); if (text.Length == 0 || text.Length > 2) { throw new SyntaxErrorException(in start, cursor.Mark(), "While scanning a tag, found an incorrect UTF-8 sequence."); } return text; } private string ScanTagHandle(bool isDirective, Mark start) { if (!analyzer.Check('!')) { throw new SyntaxErrorException(in start, cursor.Mark(), "While scanning a tag, did not find expected '!'."); } StringBuilderPool.BuilderWrapper builderWrapper = StringBuilderPool.Rent(); try { StringBuilder builder = builderWrapper.Builder; builder.Append(ReadCurrentCharacter()); while (analyzer.IsAlphaNumericDashOrUnderscore()) { builder.Append(ReadCurrentCharacter()); } if (analyzer.Check('!')) { builder.Append(ReadCurrentCharacter()); } else if (isDirective && (builder.Length != 1 || builder[0] != '!')) { throw new SyntaxErrorException(in start, cursor.Mark(), "While scanning a tag directive, did not find expected '!'."); } return builder.ToString(); } finally { ((IDisposable)builderWrapper/*cast due to .constrained prefix*/).Dispose(); } } private int ScanVersionDirectiveNumber(in Mark start) { int num = 0; int num2 = 0; while (analyzer.IsDigit()) { if (++num2 > 9) { throw new SyntaxErrorException(in start, cursor.Mark(), "While scanning a %YAML directive, found extremely long version number."); } num = num * 10 + analyzer.AsDigit(); Skip(); } if (num2 == 0) { throw new SyntaxErrorException(in start, cursor.Mark(), "While scanning a %YAML directive, did not find expected version number."); } return num; } private void SaveSimpleKey() { bool isRequired = flowLevel == 0 && indent == cursor.LineOffset; if (simpleKeyAllowed) { SimpleKey item = new SimpleKey(isRequired, tokensParsed + tokens.Count, cursor); RemoveSimpleKey(); simpleKeys.Pop(); simpleKeys.Push(item); } } } internal class SemanticErrorException : YamlException { public SemanticErrorException(string message) : base(message) { } public SemanticErrorException(in Mark start, in Mark end, string message) : base(in start, in end, message) { } public SemanticErrorException(string message, Exception inner) : base(message, inner) { } } internal sealed class SimpleKey { private readonly Cursor cursor; public bool IsPossible { get; private set; } public bool IsRequired { get; } public int TokenNumber { get; } public long Index => cursor.Index; public long Line => cursor.Line; public long LineOffset => cursor.LineOffset; public Mark Mark => cursor.Mark(); public void MarkAsImpossible() { IsPossible = false; } public SimpleKey() { cursor = new Cursor(); } public SimpleKey(bool isRequired, int tokenNumber, Cursor cursor) { IsPossible = true; IsRequired = isRequired; TokenNumber = tokenNumber; this.cursor = new Cursor(cursor); } } internal sealed class StringLookAheadBuffer : ILookAheadBuffer, IResettable { public string Value { get; set; } = string.Empty; public int Position { get; private set; } public int Length => Value.Length; public bool EndOfInput => IsOutside(Position); public char Peek(int offset) { int index = Position + offset; if (!IsOutside(index)) { return Value[index]; } return '\0'; } private bool IsOutside(int index) { return index >= Value.Length; } public void Skip(int length) { if (length < 0) { throw new ArgumentOutOfRangeException("length", "The length must be positive."); } Position += length; } public bool TryReset() { Position = 0; Value = string.Empty; return true; } } internal sealed class SyntaxErrorException : YamlException { public SyntaxErrorException(string message) : base(message) { } public SyntaxErrorException(in Mark start, in Mark end, string message) : base(in start, in end, message) { } public SyntaxErrorException(string message, Exception inner) : base(message, inner) { } } internal sealed class TagDirectiveCollection : KeyedCollection { public TagDirectiveCollection() { } public TagDirectiveCollection(IEnumerable tagDirectives) { foreach (TagDirective tagDirective in tagDirectives) { Add(tagDirective); } } protected override string GetKeyForItem(TagDirective item) { return item.Handle; } public new bool Contains(TagDirective directive) { return Contains(GetKeyForItem(directive)); } } internal readonly struct TagName : IEquatable { public static readonly TagName Empty; private readonly string? value; public string Value => value ?? throw new InvalidOperationException("Cannot read the Value of a non-specific tag"); public bool IsEmpty => value == null; public bool IsNonSpecific { get { if (!IsEmpty) { if (!(value == "!")) { return value == "?"; } return true; } return false; } } public bool IsLocal { get { if (!IsEmpty) { return Value[0] == '!'; } return false; } } public bool IsGlobal { get { if (!IsEmpty) { return !IsLocal; } return false; } } public TagName(string value) { this.value = value ?? throw new ArgumentNullException("value"); if (value.Length == 0) { throw new ArgumentException("Tag value must not be empty.", "value"); } if (IsGlobal && !Uri.IsWellFormedUriString(value, UriKind.RelativeOrAbsolute)) { throw new ArgumentException("Global tags must be valid URIs.", "value"); } } public override string ToString() { return value ?? "?"; } public bool Equals(TagName other) { return object.Equals(value, other.value); } public override bool Equals(object? obj) { if (obj is TagName other) { return Equals(other); } return false; } public override int GetHashCode() { return value?.GetHashCode() ?? 0; } public static bool operator ==(TagName left, TagName right) { return left.Equals(right); } public static bool operator !=(TagName left, TagName right) { return !(left == right); } public static bool operator ==(TagName left, string right) { return object.Equals(left.value, right); } public static bool operator !=(TagName left, string right) { return !(left == right); } public static implicit operator TagName(string? value) { if (value != null) { return new TagName(value); } return Empty; } } internal sealed class Version { public int Major { get; } public int Minor { get; } public Version(int major, int minor) { if (major < 0) { throw new ArgumentOutOfRangeException("major", $"{major} should be >= 0"); } Major = major; if (minor < 0) { throw new ArgumentOutOfRangeException("minor", $"{minor} should be >= 0"); } Minor = minor; } public override bool Equals(object? obj) { if (obj is Version version && Major == version.Major) { return Minor == version.Minor; } return false; } public override int GetHashCode() { return HashCode.CombineHashCodes(Major.GetHashCode(), Minor.GetHashCode()); } } internal class YamlException : Exception { public Mark Start { get; } public Mark End { get; } public YamlException(string message) : this(in Mark.Empty, in Mark.Empty, message) { } public YamlException(in Mark start, in Mark end, string message) : this(in start, in end, message, null) { } public YamlException(in Mark start, in Mark end, string message, Exception? innerException) : base(message, innerException) { Start = start; End = end; } public YamlException(string message, Exception inner) : this(in Mark.Empty, in Mark.Empty, message, inner) { } public override string ToString() { return $"({Start}) - ({End}): {Message}"; } } } namespace YamlDotNet.Core.Tokens { internal class Anchor : Token { public AnchorName Value { get; } public Anchor(AnchorName value) : this(value, Mark.Empty, Mark.Empty) { } public Anchor(AnchorName value, Mark start, Mark end) : base(in start, in end) { if (value.IsEmpty) { throw new ArgumentNullException("value"); } Value = value; } } internal sealed class AnchorAlias : Token { public AnchorName Value { get; } public AnchorAlias(AnchorName value) : this(value, Mark.Empty, Mark.Empty) { } public AnchorAlias(AnchorName value, Mark start, Mark end) : base(in start, in end) { if (value.IsEmpty) { throw new ArgumentNullException("value"); } Value = value; } } internal sealed class BlockEnd : Token { public BlockEnd() : this(in Mark.Empty, in Mark.Empty) { } public BlockEnd(in Mark start, in Mark end) : base(in start, in end) { } } internal sealed class BlockEntry : Token { public BlockEntry() : this(in Mark.Empty, in Mark.Empty) { } public BlockEntry(in Mark start, in Mark end) : base(in start, in end) { } } internal sealed class BlockMappingStart : Token { public BlockMappingStart() : this(in Mark.Empty, in Mark.Empty) { } public BlockMappingStart(in Mark start, in Mark end) : base(in start, in end) { } } internal sealed class BlockSequenceStart : Token { public BlockSequenceStart() : this(in Mark.Empty, in Mark.Empty) { } public BlockSequenceStart(in Mark start, in Mark end) : base(in start, in end) { } } internal sealed class Comment : Token { public string Value { get; } public bool IsInline { get; } public Comment(string value, bool isInline) : this(value, isInline, Mark.Empty, Mark.Empty) { } public Comment(string value, bool isInline, Mark start, Mark end) : base(in start, in end) { Value = value ?? throw new ArgumentNullException("value"); IsInline = isInline; } } internal sealed class DocumentEnd : Token { public DocumentEnd() : this(in Mark.Empty, in Mark.Empty) { } public DocumentEnd(in Mark start, in Mark end) : base(in start, in end) { } } internal sealed class DocumentStart : Token { public DocumentStart() : this(in Mark.Empty, in Mark.Empty) { } public DocumentStart(in Mark start, in Mark end) : base(in start, in end) { } } internal class Error : Token { public string Value { get; } public Error(string value, Mark start, Mark end) : base(in start, in end) { Value = value; } } internal sealed class FlowEntry : Token { public FlowEntry() : this(in Mark.Empty, in Mark.Empty) { } public FlowEntry(in Mark start, in Mark end) : base(in start, in end) { } } internal sealed class FlowMappingEnd : Token { public FlowMappingEnd() : this(in Mark.Empty, in Mark.Empty) { } public FlowMappingEnd(in Mark start, in Mark end) : base(in start, in end) { } } internal sealed class FlowMappingStart : Token { public FlowMappingStart() : this(in Mark.Empty, in Mark.Empty) { } public FlowMappingStart(in Mark start, in Mark end) : base(in start, in end) { } } internal sealed class FlowSequenceEnd : Token { public FlowSequenceEnd() : this(in Mark.Empty, in Mark.Empty) { } public FlowSequenceEnd(in Mark start, in Mark end) : base(in start, in end) { } } internal sealed class FlowSequenceStart : Token { public FlowSequenceStart() : this(in Mark.Empty, in Mark.Empty) { } public FlowSequenceStart(in Mark start, in Mark end) : base(in start, in end) { } } internal sealed class Key : Token { public Key() : this(in Mark.Empty, in Mark.Empty) { } public Key(in Mark start, in Mark end) : base(in start, in end) { } } internal sealed class Scalar : Token { public bool IsKey { get; set; } public string Value { get; } public ScalarStyle Style { get; } public Scalar(string value) : this(value, ScalarStyle.Any) { } public Scalar(string value, ScalarStyle style) : this(value, style, Mark.Empty, Mark.Empty) { } public Scalar(string value, ScalarStyle style, Mark start, Mark end) : base(in start, in end) { Value = value ?? throw new ArgumentNullException("value"); Style = style; } } internal sealed class StreamEnd : Token { public StreamEnd() : this(in Mark.Empty, in Mark.Empty) { } public StreamEnd(in Mark start, in Mark end) : base(in start, in end) { } } internal sealed class StreamStart : Token { public StreamStart() : this(in Mark.Empty, in Mark.Empty) { } public StreamStart(in Mark start, in Mark end) : base(in start, in end) { } } internal sealed class Tag : Token { public string Handle { get; } public string Suffix { get; } public Tag(string handle, string suffix) : this(handle, suffix, Mark.Empty, Mark.Empty) { } public Tag(string handle, string suffix, Mark start, Mark end) : base(in start, in end) { Handle = handle ?? throw new ArgumentNullException("handle"); Suffix = suffix ?? throw new ArgumentNullException("suffix"); } } internal class TagDirective : Token { private static readonly Regex TagHandlePattern = new Regex("^!([0-9A-Za-z_\\-]*!)?$", RegexOptions.Compiled); public string Handle { get; } public string Prefix { get; } public TagDirective(string handle, string prefix) : this(handle, prefix, Mark.Empty, Mark.Empty) { } public TagDirective(string handle, string prefix, Mark start, Mark end) : base(in start, in end) { if (string.IsNullOrEmpty(handle)) { throw new ArgumentNullException("handle", "Tag handle must not be empty."); } if (!TagHandlePattern.IsMatch(handle)) { throw new ArgumentException("Tag handle must start and end with '!' and contain alphanumerical characters only.", "handle"); } Handle = handle; if (string.IsNullOrEmpty(prefix)) { throw new ArgumentNullException("prefix", "Tag prefix must not be empty."); } Prefix = prefix; } public override bool Equals(object? obj) { if (obj is TagDirective tagDirective && Handle.Equals(tagDirective.Handle)) { return Prefix.Equals(tagDirective.Prefix); } return false; } public override int GetHashCode() { return Handle.GetHashCode() ^ Prefix.GetHashCode(); } public override string ToString() { return Handle + " => " + Prefix; } } internal abstract class Token { public Mark Start { get; } public Mark End { get; } protected Token(in Mark start, in Mark end) { Start = start; End = end; } } internal sealed class Value : Token { public Value() : this(in Mark.Empty, in Mark.Empty) { } public Value(in Mark start, in Mark end) : base(in start, in end) { } } internal sealed class VersionDirective : Token { public Version Version { get; } public VersionDirective(Version version) : this(version, Mark.Empty, Mark.Empty) { } public VersionDirective(Version version, Mark start, Mark end) : base(in start, in end) { Version = version; } public override bool Equals(object? obj) { if (obj is VersionDirective versionDirective) { return Version.Equals(versionDirective.Version); } return false; } public override int GetHashCode() { return Version.GetHashCode(); } } } namespace YamlDotNet.Core.ObjectPool { internal class DefaultObjectPool : ObjectPool where T : class { private readonly Func createFunc; private readonly Func returnFunc; private readonly int maxCapacity; private int numItems; private protected readonly ConcurrentQueue items = new ConcurrentQueue(); private protected T? fastItem; public DefaultObjectPool(IPooledObjectPolicy policy) : this(policy, Environment.ProcessorCount * 2) { } public DefaultObjectPool(IPooledObjectPolicy policy, int maximumRetained) { createFunc = policy.Create; returnFunc = policy.Return; maxCapacity = maximumRetained - 1; } public override T Get() { T result = fastItem; if (result == null || Interlocked.CompareExchange(ref fastItem, null, result) != result) { if (items.TryDequeue(out result)) { Interlocked.Decrement(ref numItems); return result; } return createFunc(); } return result; } public override void Return(T obj) { ReturnCore(obj); } private protected bool ReturnCore(T obj) { if (!returnFunc(obj)) { return false; } if (fastItem != null || Interlocked.CompareExchange(ref fastItem, obj, null) != null) { if (Interlocked.Increment(ref numItems) <= maxCapacity) { items.Enqueue(obj); return true; } Interlocked.Decrement(ref numItems); return false; } return true; } } internal class DefaultPooledObjectPolicy : IPooledObjectPolicy where T : class, new() { public T Create() { return new T(); } public bool Return(T obj) { if (obj is IResettable resettable) { return resettable.TryReset(); } return true; } } internal interface IPooledObjectPolicy where T : notnull { T Create(); bool Return(T obj); } internal interface IResettable { bool TryReset(); } internal abstract class ObjectPool where T : class { public abstract T Get(); public abstract void Return(T obj); } internal static class ObjectPool { public static ObjectPool Create(IPooledObjectPolicy? policy = null) where T : class, new() { return new DefaultObjectPool(policy ?? new DefaultPooledObjectPolicy()); } public static ObjectPool Create(int maximumRetained, IPooledObjectPolicy? policy = null) where T : class, new() { return new DefaultObjectPool(policy ?? new DefaultPooledObjectPolicy(), maximumRetained); } } [DebuggerStepThrough] internal static class StringBuilderPool { internal readonly struct BuilderWrapper : IDisposable { public readonly StringBuilder Builder; private readonly ObjectPool pool; public BuilderWrapper(StringBuilder builder, ObjectPool pool) { Builder = builder; this.pool = pool; } public override string ToString() { return Builder.ToString(); } public void Dispose() { pool.Return(Builder); } } private static readonly ObjectPool Pool = ObjectPool.Create(new StringBuilderPooledObjectPolicy { InitialCapacity = 16, MaximumRetainedCapacity = 1024 }); public static BuilderWrapper Rent() { StringBuilder builder = Pool.Get(); return new BuilderWrapper(builder, Pool); } } internal class StringBuilderPooledObjectPolicy : IPooledObjectPolicy { public int InitialCapacity { get; set; } = 100; public int MaximumRetainedCapacity { get; set; } = 4096; public StringBuilder Create() { return new StringBuilder(InitialCapacity); } public bool Return(StringBuilder obj) { if (obj.Capacity > MaximumRetainedCapacity) { return false; } obj.Clear(); return true; } } internal static class StringLookAheadBufferPool { internal readonly struct BufferWrapper : IDisposable { public readonly StringLookAheadBuffer Buffer; private readonly ObjectPool pool; public BufferWrapper(StringLookAheadBuffer buffer, ObjectPool pool) { Buffer = buffer; this.pool = pool; } public override string ToString() { return Buffer.ToString(); } public void Dispose() { pool.Return(Buffer); } } private static readonly ObjectPool Pool = ObjectPool.Create(new DefaultPooledObjectPolicy()); public static BufferWrapper Rent(string value) { StringLookAheadBuffer stringLookAheadBuffer = Pool.Get(); stringLookAheadBuffer.Value = value; return new BufferWrapper(stringLookAheadBuffer, Pool); } } } namespace YamlDotNet.Core.Events { internal sealed class AnchorAlias : ParsingEvent { internal override EventType Type => EventType.Alias; public AnchorName Value { get; } public AnchorAlias(AnchorName value, Mark start, Mark end) : base(in start, in end) { if (value.IsEmpty) { throw new YamlException(in start, in end, "Anchor value must not be empty."); } Value = value; } public AnchorAlias(AnchorName value) : this(value, Mark.Empty, Mark.Empty) { } public override string ToString() { return $"Alias [value = {Value}]"; } public override void Accept(IParsingEventVisitor visitor) { visitor.Visit(this); } } internal sealed class Comment : ParsingEvent { public string Value { get; } public bool IsInline { get; } internal override EventType Type => EventType.Comment; public Comment(string value, bool isInline) : this(value, isInline, Mark.Empty, Mark.Empty) { } public Comment(string value, bool isInline, Mark start, Mark end) : base(in start, in end) { Value = value; IsInline = isInline; } public override void Accept(IParsingEventVisitor visitor) { visitor.Visit(this); } public override string ToString() { return (IsInline ? "Inline" : "Block") + " Comment [" + Value + "]"; } } internal sealed class DocumentEnd : ParsingEvent { public override int NestingIncrease => -1; internal override EventType Type => EventType.DocumentEnd; public bool IsImplicit { get; } public DocumentEnd(bool isImplicit, Mark start, Mark end) : base(in start, in end) { IsImplicit = isImplicit; } public DocumentEnd(bool isImplicit) : this(isImplicit, Mark.Empty, Mark.Empty) { } public override string ToString() { return $"Document end [isImplicit = {IsImplicit}]"; } public override void Accept(IParsingEventVisitor visitor) { visitor.Visit(this); } } internal sealed class DocumentStart : ParsingEvent { public override int NestingIncrease => 1; internal override EventType Type => EventType.DocumentStart; public TagDirectiveCollection? Tags { get; } public VersionDirective? Version { get; } public bool IsImplicit { get; } public DocumentStart(VersionDirective? version, TagDirectiveCollection? tags, bool isImplicit, Mark start, Mark end) : base(in start, in end) { Version = version; Tags = tags; IsImplicit = isImplicit; } public DocumentStart(VersionDirective? version, TagDirectiveCollection? tags, bool isImplicit) : this(version, tags, isImplicit, Mark.Empty, Mark.Empty) { } public DocumentStart(in Mark start, in Mark end) : this(null, null, isImplicit: true, start, end) { } public DocumentStart() : this(null, null, isImplicit: true, Mark.Empty, Mark.Empty) { } public override string ToString() { return $"Document start [isImplicit = {IsImplicit}]"; } public override void Accept(IParsingEventVisitor visitor) { visitor.Visit(this); } } internal enum EventType { None, StreamStart, StreamEnd, DocumentStart, DocumentEnd, Alias, Scalar, SequenceStart, SequenceEnd, MappingStart, MappingEnd, Comment } internal interface IParsingEventVisitor { void Visit(AnchorAlias e); void Visit(StreamStart e); void Visit(StreamEnd e); void Visit(DocumentStart e); void Visit(DocumentEnd e); void Visit(Scalar e); void Visit(SequenceStart e); void Visit(SequenceEnd e); void Visit(MappingStart e); void Visit(MappingEnd e); void Visit(Comment e); } internal class MappingEnd : ParsingEvent { public override int NestingIncrease => -1; internal override EventType Type => EventType.MappingEnd; public MappingEnd(in Mark start, in Mark end) : base(in start, in end) { } public MappingEnd() : this(in Mark.Empty, in Mark.Empty) { } public override string ToString() { return "Mapping end"; } public override void Accept(IParsingEventVisitor visitor) { visitor.Visit(this); } } internal sealed class MappingStart : NodeEvent { public override int NestingIncrease => 1; internal override EventType Type => EventType.MappingStart; public bool IsImplicit { get; } public override bool IsCanonical => !IsImplicit; public MappingStyle Style { get; } public MappingStart(AnchorName anchor, TagName tag, bool isImplicit, MappingStyle style, Mark start, Mark end) : base(anchor, tag, start, end) { IsImplicit = isImplicit; Style = style; } public MappingStart(AnchorName anchor, TagName tag, bool isImplicit, MappingStyle style) : this(anchor, tag, isImplicit, style, Mark.Empty, Mark.Empty) { } public MappingStart() : this(AnchorName.Empty, TagName.Empty, isImplicit: true, MappingStyle.Any, Mark.Empty, Mark.Empty) { } public override string ToString() { return $"Mapping start [anchor = {base.Anchor}, tag = {base.Tag}, isImplicit = {IsImplicit}, style = {Style}]"; } public override void Accept(IParsingEventVisitor visitor) { visitor.Visit(this); } } internal enum MappingStyle { Any, Block, Flow } internal abstract class NodeEvent : ParsingEvent { public AnchorName Anchor { get; } public TagName Tag { get; } public abstract bool IsCanonical { get; } protected NodeEvent(AnchorName anchor, TagName tag, Mark start, Mark end) : base(in start, in end) { Anchor = anchor; Tag = tag; } protected NodeEvent(AnchorName anchor, TagName tag) : this(anchor, tag, Mark.Empty, Mark.Empty) { } } internal abstract class ParsingEvent { public virtual int NestingIncrease => 0; internal abstract EventType Type { get; } public Mark Start { get; } public Mark End { get; } public abstract void Accept(IParsingEventVisitor visitor); internal ParsingEvent(in Mark start, in Mark end) { Start = start; End = end; } } internal sealed class Scalar : NodeEvent { internal override EventType Type => EventType.Scalar; public string Value { get; } public ScalarStyle Style { get; } public bool IsPlainImplicit { get; } public bool IsQuotedImplicit { get; } public override bool IsCanonical { get { if (!IsPlainImplicit) { return !IsQuotedImplicit; } return false; } } public bool IsKey { get; } public Scalar(AnchorName anchor, TagName tag, string value, ScalarStyle style, bool isPlainImplicit, bool isQuotedImplicit, Mark start, Mark end, bool isKey = false) : base(anchor, tag, start, end) { Value = value; Style = style; IsPlainImplicit = isPlainImplicit; IsQuotedImplicit = isQuotedImplicit; IsKey = isKey; } public Scalar(AnchorName anchor, TagName tag, string value, ScalarStyle style, bool isPlainImplicit, bool isQuotedImplicit) : this(anchor, tag, value, style, isPlainImplicit, isQuotedImplicit, Mark.Empty, Mark.Empty) { } public Scalar(string value) : this(AnchorName.Empty, TagName.Empty, value, ScalarStyle.Any, isPlainImplicit: true, isQuotedImplicit: true, Mark.Empty, Mark.Empty) { } public Scalar(TagName tag, string value) : this(AnchorName.Empty, tag, value, ScalarStyle.Any, isPlainImplicit: true, isQuotedImplicit: true, Mark.Empty, Mark.Empty) { } public Scalar(AnchorName anchor, TagName tag, string value) : this(anchor, tag, value, ScalarStyle.Any, isPlainImplicit: true, isQuotedImplicit: true, Mark.Empty, Mark.Empty) { } public override string ToString() { return $"Scalar [anchor = {base.Anchor}, tag = {base.Tag}, value = {Value}, style = {Style}, isPlainImplicit = {IsPlainImplicit}, isQuotedImplicit = {IsQuotedImplicit}]"; } public override void Accept(IParsingEventVisitor visitor) { visitor.Visit(this); } } internal sealed class SequenceEnd : ParsingEvent { public override int NestingIncrease => -1; internal override EventType Type => EventType.SequenceEnd; public SequenceEnd(in Mark start, in Mark end) : base(in start, in end) { } public SequenceEnd() : this(in Mark.Empty, in Mark.Empty) { } public override string ToString() { return "Sequence end"; } public override void Accept(IParsingEventVisitor visitor) { visitor.Visit(this); } } internal sealed class SequenceStart : NodeEvent { public override int NestingIncrease => 1; internal override EventType Type => EventType.SequenceStart; public bool IsImplicit { get; } public override bool IsCanonical => !IsImplicit; public SequenceStyle Style { get; } public SequenceStart(AnchorName anchor, TagName tag, bool isImplicit, SequenceStyle style, Mark start, Mark end) : base(anchor, tag, start, end) { IsImplicit = isImplicit; Style = style; } public SequenceStart(AnchorName anchor, TagName tag, bool isImplicit, SequenceStyle style) : this(anchor, tag, isImplicit, style, Mark.Empty, Mark.Empty) { } public override string ToString() { return $"Sequence start [anchor = {base.Anchor}, tag = {base.Tag}, isImplicit = {IsImplicit}, style = {Style}]"; } public override void Accept(IParsingEventVisitor visitor) { visitor.Visit(this); } } internal enum SequenceStyle { Any, Block, Flow } internal sealed class StreamEnd : ParsingEvent { public override int NestingIncrease => -1; internal override EventType Type => EventType.StreamEnd; public StreamEnd(in Mark start, in Mark end) : base(in start, in end) { } public StreamEnd() : this(in Mark.Empty, in Mark.Empty) { } public override string ToString() { return "Stream end"; } public override void Accept(IParsingEventVisitor visitor) { visitor.Visit(this); } } internal sealed class StreamStart : ParsingEvent { public override int NestingIncrease => 1; internal override EventType Type => EventType.StreamStart; public StreamStart() : this(in Mark.Empty, in Mark.Empty) { } public StreamStart(in Mark start, in Mark end) : base(in start, in end) { } public override string ToString() { return "Stream start"; } public override void Accept(IParsingEventVisitor visitor) { visitor.Visit(this); } } } namespace System.Diagnostics.CodeAnalysis { [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter, Inherited = false)] [ExcludeFromCodeCoverage] [DebuggerNonUserCode] internal sealed class AllowNullAttribute : Attribute { } [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter, Inherited = false)] [ExcludeFromCodeCoverage] [DebuggerNonUserCode] internal sealed class DisallowNullAttribute : Attribute { } [AttributeUsage(AttributeTargets.Method, Inherited = false)] [ExcludeFromCodeCoverage] [DebuggerNonUserCode] internal sealed class DoesNotReturnAttribute : Attribute { } [AttributeUsage(AttributeTargets.Parameter, Inherited = false)] [ExcludeFromCodeCoverage] [DebuggerNonUserCode] internal sealed class DoesNotReturnIfAttribute : Attribute { public bool ParameterValue { get; } public DoesNotReturnIfAttribute(bool parameterValue) { ParameterValue = parameterValue; } } [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.ReturnValue, Inherited = false)] [ExcludeFromCodeCoverage] [DebuggerNonUserCode] internal sealed class MaybeNullAttribute : Attribute { } [AttributeUsage(AttributeTargets.Parameter, Inherited = false)] [ExcludeFromCodeCoverage] [DebuggerNonUserCode] internal sealed class MaybeNullWhenAttribute : Attribute { public bool ReturnValue { get; } public MaybeNullWhenAttribute(bool returnValue) { ReturnValue = returnValue; } } [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, Inherited = false, AllowMultiple = true)] [ExcludeFromCodeCoverage] [DebuggerNonUserCode] internal sealed class MemberNotNullAttribute : Attribute { public string[] Members { get; } public MemberNotNullAttribute(string member) { Members = new string[1] { member }; } public MemberNotNullAttribute(params string[] members) { Members = members; } } [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, Inherited = false, AllowMultiple = true)] [ExcludeFromCodeCoverage] [DebuggerNonUserCode] internal sealed class MemberNotNullWhenAttribute : Attribute { public bool ReturnValue { get; } public string[] Members { get; } public MemberNotNullWhenAttribute(bool returnValue, string member) { ReturnValue = returnValue; Members = new string[1] { member }; } public MemberNotNullWhenAttribute(bool returnValue, params string[] members) { ReturnValue = returnValue; Members = members; } } [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.ReturnValue, Inherited = false)] [ExcludeFromCodeCoverage] [DebuggerNonUserCode] internal sealed class NotNullAttribute : Attribute { } [AttributeUsage(AttributeTargets.Property | AttributeTargets.Parameter | AttributeTargets.ReturnValue, AllowMultiple = true, Inherited = false)] [ExcludeFromCodeCoverage] [DebuggerNonUserCode] internal sealed class NotNullIfNotNullAttribute : Attribute { public string ParameterName { get; } public NotNullIfNotNullAttribute(string parameterName) { ParameterName = parameterName; } } [AttributeUsage(AttributeTargets.Parameter, Inherited = false)] [ExcludeFromCodeCoverage] [DebuggerNonUserCode] internal sealed class NotNullWhenAttribute : Attribute { public bool ReturnValue { get; } public NotNullWhenAttribute(bool returnValue) { ReturnValue = returnValue; } } }