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.Configuration; using BepInEx.Logging; using HarmonyLib; using JetBrains.Annotations; 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.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("BossRules")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("sighsorry")] [assembly: AssemblyProduct("BossRules")] [assembly: AssemblyCopyright("Copyright (c) 2026")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("E72C88F7-8DC7-45B4-A56E-5E25F182EF28")] [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 BossRules { [BepInPlugin("sighsorry.BossRules", "BossRules", "1.0.0")] public sealed class BossRulesPlugin : BaseUnityPlugin { public enum Toggle { On = 1, Off = 0 } private sealed class ConfigurationManagerAttributes { public int? Order; } internal const string ModName = "BossRules"; internal const string ModVersion = "1.0.0"; internal const string Author = "sighsorry"; internal const string ModGUID = "sighsorry.BossRules"; internal const string AltarYamlFileName = "BossRules.altar.yml"; internal const string AltarReferenceYamlFileName = "BossRules.altar.reference.yml"; internal const string RulesYamlFileName = "BossRules.yml"; internal const string ForsakenPowersYamlFileName = "BossRules.forsakenPowers.yml"; private const float FileReloadDebounceSeconds = 0.25f; internal static readonly ManualLogSource BossRulesLogger = Logger.CreateLogSource("BossRules"); private static ConfigSync? _configSync; private readonly Harmony _harmony = new Harmony("sighsorry.BossRules"); private CustomSyncedValue _syncedAltarYaml; private CustomSyncedValue _syncedRulesYaml; private CustomSyncedValue _syncedForsakenPowersYaml; private FileSystemWatcher? _watcher; private float _reloadDueAt = -1f; private ConfigEntry _lockConfiguration; private IReadOnlyList _altarEntries = Array.Empty(); private BossRuleConfigurationState _rulesConfiguration = BossRuleConfigurationState.Empty; private IReadOnlyList _forsakenPowers = Array.Empty(); internal static BossRulesPlugin? Instance { get; private set; } internal static ConfigSync ConfigSync => _configSync ?? throw new InvalidOperationException("ServerSync has not been initialized yet."); internal static string ConfigDirectoryPath => Path.Combine(Paths.ConfigPath, "BossRules"); internal static string AltarYamlFilePath => Path.Combine(ConfigDirectoryPath, "BossRules.altar.yml"); internal static string AltarReferenceYamlFilePath => Path.Combine(ConfigDirectoryPath, "BossRules.altar.reference.yml"); internal static string RulesYamlFilePath => Path.Combine(ConfigDirectoryPath, "BossRules.yml"); internal static string ForsakenPowersYamlFilePath => Path.Combine(ConfigDirectoryPath, "BossRules.forsakenPowers.yml"); internal static bool IsSourceOfTruth => ConfigSync.IsSourceOfTruth; internal static IReadOnlyList AltarEntries => Instance?._altarEntries ?? Array.Empty(); internal static BossRuleConfigurationState RulesConfiguration => Instance?._rulesConfiguration ?? BossRuleConfigurationState.Empty; internal static bool IsRuntimeServer() { if ((Object)(object)ZNet.instance != (Object)null) { return ZNet.instance.IsServer(); } return false; } private void Awake() { EnsureServerSyncInitialized(); Instance = this; Directory.CreateDirectory(ConfigDirectoryPath); AltarConfigurationFiles.EnsureDefaultFiles(); BossRuleConfigurationFiles.EnsureDefaultFile(); ForsakenPowerConfigurationFiles.EnsureDefaultFile(); BindConfiguration(); _syncedAltarYaml = new CustomSyncedValue(ConfigSync, "altar-yaml", "", 50); _syncedAltarYaml.ValueChanged += HandleSyncedAltarYamlChanged; _syncedRulesYaml = new CustomSyncedValue(ConfigSync, "rules-yaml", "", 60); _syncedRulesYaml.ValueChanged += HandleSyncedRulesYamlChanged; _syncedForsakenPowersYaml = new CustomSyncedValue(ConfigSync, "forsaken-powers-yaml", "", 65); _syncedForsakenPowersYaml.ValueChanged += HandleSyncedForsakenPowersYamlChanged; ConfigSync.SourceOfTruthChanged += HandleSourceOfTruthChanged; LoadLocalAltarYamlAndPublish("startup"); LoadLocalRulesYamlAndPublish("startup"); LoadLocalForsakenPowersYamlAndPublish("startup"); _harmony.PatchAll(typeof(BossRulesPlugin).Assembly); BossStonePerPlayerRuntime.Initialize(); BossRulesConsoleCommands.Register(); InitializeWatcher(); ((BaseUnityPlugin)this).Config.Save(); } private void Update() { ProcessQueuedYamlReload(); DataForgeStatusEffectBridge.ProcessDeferredSubscription(); AltarRuntime.ProcessPendingAltarSummonMarkers(); AltarRuntime.ProcessDeferredReapply(); AltarReferenceGenerator.TryAutoRefreshReferenceConfigurationFile(); ForsakenPowerRuntime.ProcessDeferredApply(); BossStonePerPlayerRuntime.EnsureRpcRegistered(); BossStonePerPlayerRuntime.ProcessPendingResetRequests(); DespawnRulesManager.ExecuteServerTick(); BossTamedPressureRuntime.ExecuteServerTick(); } private void OnDestroy() { if ((Object)(object)Instance == (Object)(object)this) { Instance = null; } if (_configSync != null) { ConfigSync.SourceOfTruthChanged -= HandleSourceOfTruthChanged; } _syncedAltarYaml.ValueChanged -= HandleSyncedAltarYamlChanged; _syncedRulesYaml.ValueChanged -= HandleSyncedRulesYamlChanged; _syncedForsakenPowersYaml.ValueChanged -= HandleSyncedForsakenPowersYamlChanged; _watcher?.Dispose(); _watcher = null; AltarRuntime.Shutdown(); BossStonePerPlayerRuntime.Shutdown(); AltarReferenceGenerator.ResetAutoRefresh(); BossRulesManager.ClearRuntimeState(); BossRulesRuntime.Reset(); DataForgeStatusEffectBridge.Shutdown(); _harmony.UnpatchSelf(); ((BaseUnityPlugin)this).Config.Save(); } private static void EnsureServerSyncInitialized() { if (_configSync == null) { _configSync = new ConfigSync("sighsorry.BossRules") { DisplayName = "BossRules", CurrentVersion = "1.0.0", MinimumRequiredVersion = "1.0.0" }; } } private void BindConfiguration() { bool saveOnConfigSet = ((BaseUnityPlugin)this).Config.SaveOnConfigSet; ((BaseUnityPlugin)this).Config.SaveOnConfigSet = false; try { _lockConfiguration = BindConfigEntry("1 - General", "Lock Configuration", Toggle.On, "If on, synced configuration can be changed by server admins only.", synchronizedSetting: true, 200); BossRulesConfig.Bind(this); ConfigSync.AddLockingConfigEntry(_lockConfiguration); } finally { ((BaseUnityPlugin)this).Config.SaveOnConfigSet = saveOnConfigSet; } } internal ConfigEntry BindConfigEntry(string group, string name, T value, string description, bool synchronizedSetting = true, int? configManagerOrder = null) { //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Expected O, but got Unknown ConfigDescription val = new ConfigDescription(description + (synchronizedSetting ? " [Synced with Server]" : " [Not Synced with Server]"), (AcceptableValueBase)null, BuildConfigDescriptionTags(configManagerOrder)); ConfigEntry val2 = ((BaseUnityPlugin)this).Config.Bind(group, name, value, val); ConfigSync.AddConfigEntry(val2).SynchronizedConfig = synchronizedSetting; return val2; } private static object[] BuildConfigDescriptionTags(int? configManagerOrder) { if (!configManagerOrder.HasValue) { return Array.Empty(); } return new object[1] { new ConfigurationManagerAttributes { Order = configManagerOrder.Value } }; } private void InitializeWatcher() { _watcher = new FileSystemWatcher(ConfigDirectoryPath, "*.yml") { IncludeSubdirectories = false, NotifyFilter = (NotifyFilters.FileName | NotifyFilters.Size | NotifyFilters.LastWrite | NotifyFilters.CreationTime) }; _watcher.Changed += QueueYamlReload; _watcher.Created += QueueYamlReload; _watcher.Renamed += QueueYamlReload; _watcher.EnableRaisingEvents = true; } private void QueueYamlReload(object sender, FileSystemEventArgs args) { if (IsSourceOfTruth) { string fileName = Path.GetFileName(args.FullPath); if (string.Equals(fileName, "BossRules.altar.yml", StringComparison.OrdinalIgnoreCase) || string.Equals(fileName, "BossRules.yml", StringComparison.OrdinalIgnoreCase) || string.Equals(fileName, "BossRules.forsakenPowers.yml", StringComparison.OrdinalIgnoreCase)) { _reloadDueAt = Time.realtimeSinceStartup + 0.25f; } } } private void ProcessQueuedYamlReload() { if (!(_reloadDueAt < 0f) && !(Time.realtimeSinceStartup < _reloadDueAt)) { _reloadDueAt = -1f; LoadLocalAltarYamlAndPublish("file change"); LoadLocalRulesYamlAndPublish("file change"); LoadLocalForsakenPowersYamlAndPublish("file change"); } } private void HandleSourceOfTruthChanged(bool sourceOfTruth) { if (sourceOfTruth) { AltarReferenceGenerator.ResetAutoRefresh(); LoadLocalAltarYamlAndPublish("authority change"); LoadLocalRulesYamlAndPublish("authority change"); LoadLocalForsakenPowersYamlAndPublish("authority change"); } else { ApplyAltarYaml(_syncedAltarYaml.Value ?? "", "server sync"); ApplyRulesYaml(_syncedRulesYaml.Value ?? "", "server sync"); ApplyForsakenPowersYaml(_syncedForsakenPowersYaml.Value ?? "", "server sync"); } } private void HandleSyncedAltarYamlChanged() { if (!IsSourceOfTruth) { ApplyAltarYaml(_syncedAltarYaml.Value ?? "", "server sync"); } } private void HandleSyncedRulesYamlChanged() { if (!IsSourceOfTruth) { ApplyRulesYaml(_syncedRulesYaml.Value ?? "", "server sync"); } } private void HandleSyncedForsakenPowersYamlChanged() { if (!IsSourceOfTruth) { ApplyForsakenPowersYaml(_syncedForsakenPowersYaml.Value ?? "", "server sync"); } } private void LoadLocalAltarYamlAndPublish(string source) { AltarConfigurationFiles.EnsureDefaultFiles(); string text; try { text = File.ReadAllText(AltarYamlFilePath); } catch (Exception ex) { BossRulesLogger.LogError((object)("Failed to read " + AltarYamlFilePath + ". " + ex.GetType().Name + ": " + ex.Message)); return; } if (ApplyAltarYaml(text, source) && IsSourceOfTruth) { _syncedAltarYaml.AssignLocalValue(text); } } private void LoadLocalRulesYamlAndPublish(string source) { BossRuleConfigurationFiles.EnsureDefaultFile(); string text; try { text = File.ReadAllText(RulesYamlFilePath); } catch (Exception ex) { BossRulesLogger.LogError((object)("Failed to read " + RulesYamlFilePath + ". " + ex.GetType().Name + ": " + ex.Message)); return; } if (ApplyRulesYaml(text, source) && IsSourceOfTruth) { _syncedRulesYaml.AssignLocalValue(text); } } private void LoadLocalForsakenPowersYamlAndPublish(string source) { ForsakenPowerConfigurationFiles.EnsureDefaultFile(); string text; try { text = File.ReadAllText(ForsakenPowersYamlFilePath); } catch (Exception ex) { BossRulesLogger.LogError((object)("Failed to read " + ForsakenPowersYamlFilePath + ". " + ex.GetType().Name + ": " + ex.Message)); return; } if (ApplyForsakenPowersYaml(text, source) && IsSourceOfTruth) { _syncedForsakenPowersYaml.AssignLocalValue(text); } } private bool ApplyAltarYaml(string yaml, string source) { string text = yaml ?? ""; BossRulesDebugLog.Client($"Applying altar YAML source={source} bytes={text.Length}."); if (!AltarConfiguration.TryParse(text, source, out IReadOnlyList entries)) { return false; } _altarEntries = entries; BossRulesDebugLog.Client($"Parsed altar YAML source={source} entries={entries.Count}."); AltarRuntime.Reload(entries); return true; } private bool ApplyRulesYaml(string yaml, string source) { if (!BossRuleConfiguration.TryParse(yaml, source, out BossRuleConfigurationState state)) { return false; } _rulesConfiguration = state; BossRulesRuntime.Reload(BuildMergedRulesConfiguration()); return true; } private bool ApplyForsakenPowersYaml(string yaml, string source) { if (!ForsakenPowerConfiguration.TryParse(yaml, source, out IReadOnlyList entries)) { return false; } _forsakenPowers = entries; BossRulesRuntime.Reload(BuildMergedRulesConfiguration()); return true; } private BossRuleConfigurationState BuildMergedRulesConfiguration() { BossRuleConfigurationState bossRuleConfigurationState = new BossRuleConfigurationState(); bossRuleConfigurationState.DefaultDespawnRange = _rulesConfiguration.DefaultDespawnRange; bossRuleConfigurationState.DefaultDespawnDelaySeconds = _rulesConfiguration.DefaultDespawnDelaySeconds; bossRuleConfigurationState.DespawnRules.AddRange(_rulesConfiguration.DespawnRules); bossRuleConfigurationState.BossTamedPressureRules.AddRange(_rulesConfiguration.BossTamedPressureRules); bossRuleConfigurationState.ForsakenPowers.AddRange(_forsakenPowers); bossRuleConfigurationState.MessageDespawnStart = _rulesConfiguration.MessageDespawnStart; bossRuleConfigurationState.MessageDespawnReminder = _rulesConfiguration.MessageDespawnReminder; bossRuleConfigurationState.MessageDespawnCanceled = _rulesConfiguration.MessageDespawnCanceled; bossRuleConfigurationState.MessageBossTamedPressure = _rulesConfiguration.MessageBossTamedPressure; bossRuleConfigurationState.MessageForsakenPowerRotate = _rulesConfiguration.MessageForsakenPowerRotate; return bossRuleConfigurationState; } } internal sealed class AltarConfigurationEntry { [YamlMember(Order = 1)] public string Prefab { get; set; } = ""; [YamlMember(Order = 2)] public bool Enabled { get; set; } = true; [YamlMember(Order = 3)] public AltarOfferingBowlDefinition? OfferingBowl { get; set; } [YamlMember(Order = 4)] public List? ItemStands { get; set; } } internal sealed class AltarOfferingBowlDefinition { [YamlMember(Order = 1)] public string? BossItem { get; set; } [YamlMember(Order = 2)] public int? BossItems { get; set; } [YamlMember(Order = 3)] public string? BossPrefab { get; set; } [YamlMember(Order = 4)] public string? ItemPrefab { get; set; } [YamlMember(Order = 5)] public string? SetGlobalKey { get; set; } [YamlMember(Order = 6)] public bool? RenderSpawnAreaGizmos { get; set; } [YamlMember(Order = 7)] public bool? AlertOnSpawn { get; set; } [YamlMember(Order = 8)] public float? SpawnBossDelay { get; set; } [YamlMember(Order = 9)] public FloatRangeDefinition? SpawnBossDistance { get; set; } [YamlMember(Order = 10)] public float? SpawnBossMaxYDistance { get; set; } [YamlMember(Order = 11)] public int? GetSolidHeightMargin { get; set; } [YamlMember(Order = 12)] public bool? EnableSolidHeightCheck { get; set; } [YamlMember(Order = 13)] public float? SpawnPointClearingRadius { get; set; } [YamlMember(Order = 14)] public float? SpawnYOffset { get; set; } [YamlMember(Order = 15)] public bool? UseItemStands { get; set; } [YamlMember(Order = 16)] public string? ItemStandPrefix { get; set; } [YamlMember(Order = 17)] public float? ItemStandMaxRange { get; set; } [YamlMember(Order = 18)] public float? RespawnMinutes { get; set; } } internal sealed class AltarItemStandDefinition { [YamlMember(Order = 1)] public string? Path { get; set; } [YamlMember(Order = 2)] public bool? CanBeRemoved { get; set; } [YamlMember(Order = 3)] public bool? AutoAttach { get; set; } [YamlMember(Order = 4)] public string? OrientationType { get; set; } [YamlMember(Order = 5)] public List? SupportedTypes { get; set; } [YamlMember(Order = 6)] public List? SupportedItems { get; set; } [YamlMember(Order = 7)] public List? UnsupportedItems { get; set; } [YamlMember(Order = 8)] public float? PowerActivationDelay { get; set; } [YamlMember(Order = 9)] public string? GuardianPower { get; set; } } internal static class AltarConfiguration { private static readonly IDeserializer Deserializer = new DeserializerBuilder().WithNamingConvention(CamelCaseNamingConvention.Instance).Build(); internal static bool TryParse(string yaml, string source, out IReadOnlyList entries) { entries = Array.Empty(); try { List list = (string.IsNullOrWhiteSpace(yaml) ? new List() : Deserializer.Deserialize>(yaml)); entries = Normalize(list ?? new List()); BossRulesPlugin.BossRulesLogger.LogInfo((object)$"Loaded altar YAML from {source}: {entries.Count} entries."); return true; } catch (Exception ex) { BossRulesPlugin.BossRulesLogger.LogError((object)("Rejected altar YAML from " + source + ". Keeping the previous configuration. " + ex.GetType().Name + ": " + ex.Message)); return false; } } private static IReadOnlyList Normalize(List entries) { foreach (AltarConfigurationEntry entry in entries) { entry.Prefab = (entry.Prefab ?? "").Trim(); NormalizeOfferingBowl(entry.OfferingBowl); NormalizeItemStands(entry.ItemStands); } return entries.Where((AltarConfigurationEntry entry) => entry.Prefab.Length > 0).ToList(); } private static void NormalizeOfferingBowl(AltarOfferingBowlDefinition? definition) { if (definition != null) { definition.BossItem = NormalizeOptionalString(definition.BossItem); definition.BossPrefab = NormalizeOptionalString(definition.BossPrefab); definition.ItemPrefab = NormalizeOptionalString(definition.ItemPrefab); definition.SetGlobalKey = NormalizeOptionalString(definition.SetGlobalKey); definition.ItemStandPrefix = NormalizeOptionalString(definition.ItemStandPrefix); } } private static void NormalizeItemStands(List? definitions) { if (definitions == null) { return; } foreach (AltarItemStandDefinition definition in definitions) { definition.Path = NormalizeOptionalString(definition.Path); definition.OrientationType = NormalizeOptionalString(definition.OrientationType); definition.SupportedTypes = NormalizeStringList(definition.SupportedTypes); definition.SupportedItems = NormalizeStringList(definition.SupportedItems); definition.UnsupportedItems = NormalizeStringList(definition.UnsupportedItems); definition.GuardianPower = NormalizeOptionalString(definition.GuardianPower); } } private static List? NormalizeStringList(List? values) { List list = (from value in values?.Select((string value) => (value ?? "").Trim()) where value.Length > 0 select value).Distinct(StringComparer.OrdinalIgnoreCase).ToList(); if (list == null || list.Count <= 0) { return null; } return list; } private static string? NormalizeOptionalString(string? value) { string text = (value ?? "").Trim(); if (text.Length != 0) { return text; } return null; } } internal static class AltarConfigurationFiles { internal static void EnsureDefaultFiles() { EnsureTextFile(BossRulesPlugin.AltarYamlFilePath, BuildDefaultAltarYaml()); EnsureTextFile(BossRulesPlugin.AltarReferenceYamlFilePath, BuildReferencePlaceholderYaml()); } private static void EnsureTextFile(string path, string content) { if (!File.Exists(path)) { File.WriteAllText(path, content); BossRulesPlugin.BossRulesLogger.LogInfo((object)("Created " + path + ".")); } } private static string BuildDefaultAltarYaml() { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine("# BossRules altar overrides"); stringBuilder.AppendLine("#"); stringBuilder.AppendLine("# This file owns boss altar OfferingBowl and boss ItemStand edits."); stringBuilder.AppendLine("# It is intentionally inert by default."); stringBuilder.AppendLine("# Copy rows from BossRules.altar.reference.yml and uncomment only the fields you want to override."); stringBuilder.AppendLine("# Unless noted otherwise, null/empty/omitted override fields keep the current prefab value."); stringBuilder.AppendLine("#"); stringBuilder.AppendLine("# offeringBowl"); stringBuilder.AppendLine("#"); stringBuilder.AppendLine("# - prefab: Bonemass"); stringBuilder.AppendLine("# enabled: true # Default true when omitted."); stringBuilder.AppendLine("# offeringBowl:"); stringBuilder.AppendLine("# bossItem: null # ex) WitheredBone. Required direct offering item prefab."); stringBuilder.AppendLine("# bossItems: null # ex) 10. Number of bossItem items required; clamped to at least 1 when set."); stringBuilder.AppendLine("# bossPrefab: null # ex) Bonemass. Boss character prefab spawned after a valid offering."); stringBuilder.AppendLine("# itemPrefab: null # ex) Wishbone. Optional item reward prefab instead of spawning a boss."); stringBuilder.AppendLine("# setGlobalKey: null # ex) defeated_bonemass. Optional global key set after a valid offering."); stringBuilder.AppendLine("# renderSpawnAreaGizmos: null # ex) false. True draws the boss spawn search area while selected."); stringBuilder.AppendLine("# alertOnSpawn: null # ex) false. True calls BaseAI.Alert() on the spawned boss."); stringBuilder.AppendLine("# spawnBossDelay: null # ex) 5. Seconds to wait before spawning; clamped to at least 0."); stringBuilder.AppendLine("# spawnBossDistance: null # ex) 0~40 or {min: 0, max: 40}. Each side can be overridden separately."); stringBuilder.AppendLine("# spawnBossMaxYDistance: null # ex) 9999. Vertical spawn search distance; clamped to at least 0."); stringBuilder.AppendLine("# getSolidHeightMargin: null # ex) 1000. Terrain raycast margin; clamped to at least 0."); stringBuilder.AppendLine("# enableSolidHeightCheck: null # ex) true. True requires valid ground height."); stringBuilder.AppendLine("# spawnPointClearingRadius: null # ex) 0. Clearing radius before boss spawn; clamped to at least 0."); stringBuilder.AppendLine("# spawnYOffset: null # ex) 1. Vertical offset added to the chosen spawn position."); stringBuilder.AppendLine("# useItemStands: null # ex) true. True uses nearby ItemStands instead of direct UseItem offerings."); stringBuilder.AppendLine("# itemStandPrefix: null # ex) Boss. Object-name prefix used to select nearby ItemStands."); stringBuilder.AppendLine("# itemStandMaxRange: null # ex) 20. Max scan distance for nearby ItemStands; clamped to at least 0."); stringBuilder.AppendLine("# respawnMinutes: null # Null/omitted becomes 0, disabling BossRules altar cooldown. Set >0 for cooldown minutes."); stringBuilder.AppendLine("#"); stringBuilder.AppendLine("# itemStands"); stringBuilder.AppendLine("#"); stringBuilder.AppendLine("# - prefab: StartTemple"); stringBuilder.AppendLine("# enabled: true # Default true when omitted."); stringBuilder.AppendLine("# itemStands:"); stringBuilder.AppendLine("# - path: null # Null/empty/omitted targets all relevant stands. ex) BossStone_Eikthyr[0]/itemstand[0] targets one reference path."); stringBuilder.AppendLine("# canBeRemoved: null # ex) true. True allows players to remove the attached item."); stringBuilder.AppendLine("# autoAttach: null # ex) false. True automatically attaches compatible dropped items."); stringBuilder.AppendLine("# orientationType: null # ex) Vertical. ItemStand.Orientation name."); stringBuilder.AppendLine("# supportedTypes: [] # ex) [OneHandedWeapon, TwoHandedWeapon]. ItemDrop.ItemType names."); stringBuilder.AppendLine("# supportedItems: [] # ex) [TrophyDeer]. Explicitly allowed item prefabs."); stringBuilder.AppendLine("# unsupportedItems: [] # ex) [TrophyDeer]. Explicitly blocked item prefabs."); stringBuilder.AppendLine("# powerActivationDelay: null # ex) 2. Seconds before guardianPower activates; clamped to at least 0."); stringBuilder.AppendLine("# guardianPower: null # ex) GP_Eikthyr. StatusEffect prefab granted when used."); return stringBuilder.ToString(); } private static string BuildReferencePlaceholderYaml() { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine("# BossRules altar reference"); stringBuilder.AppendLine("#"); stringBuilder.AppendLine("# This file is generated automatically after ZoneSystem location prefabs load."); stringBuilder.AppendLine("[]"); return stringBuilder.ToString(); } } internal sealed class AltarReferenceEntry { [YamlMember(Order = 1)] public string Prefab { get; set; } = ""; [YamlMember(Order = 2)] public AltarOfferingBowlDefinition? OfferingBowl { get; set; } [YamlMember(Order = 3)] public List? ItemStands { get; set; } } internal sealed class AltarReferenceItemStandDefinition { [YamlMember(Order = 1)] public string? Path { get; set; } [YamlMember(Order = 2)] public bool? CanBeRemoved { get; set; } [YamlMember(Order = 3)] public bool? AutoAttach { get; set; } [YamlMember(Order = 4)] public string? OrientationType { get; set; } [YamlMember(Order = 5)] public FlowStringListDefinition? SupportedTypes { get; set; } [YamlMember(Order = 6)] public FlowStringListDefinition? SupportedItems { get; set; } [YamlMember(Order = 7)] public FlowStringListDefinition? UnsupportedItems { get; set; } [YamlMember(Order = 8)] public float? PowerActivationDelay { get; set; } [YamlMember(Order = 9)] public string? GuardianPower { get; set; } } internal sealed class FlowStringListDefinition : IYamlConvertible { public List Values { get; set; } = new List(); public FlowStringListDefinition() { } public FlowStringListDefinition(IEnumerable values) { Values = (from value in values where !string.IsNullOrWhiteSpace(value) select value.Trim()).ToList(); } void IYamlConvertible.Read(IParser parser, Type expectedType, ObjectDeserializer nestedObjectDeserializer) { Values.Clear(); parser.Consume(); SequenceEnd @event; while (!parser.Accept(out @event)) { Values.Add((parser.Consume().Value ?? "").Trim()); } parser.Consume(); } void IYamlConvertible.Write(IEmitter emitter, ObjectSerializer nestedObjectSerializer) { emitter.Emit(new SequenceStart(null, null, isImplicit: true, SequenceStyle.Flow)); foreach (string value in Values) { emitter.Emit(new YamlDotNet.Core.Events.Scalar(value)); } emitter.Emit(new SequenceEnd()); } } internal static class AltarReferenceGenerator { private static readonly ISerializer Serializer = new SerializerBuilder().WithNamingConvention(CamelCaseNamingConvention.Instance).ConfigureDefaultValuesHandling(DefaultValuesHandling.OmitNull | DefaultValuesHandling.OmitDefaults).Build(); private const float AutoRefreshIdleRetryDelaySeconds = 1f; private const float AutoRefreshRetryDelaySeconds = 5f; private static readonly HashSet DuplicateComponentWarnings = new HashSet(StringComparer.OrdinalIgnoreCase); private static bool _autoRefreshDone; private static float _nextAutoRefreshAttemptAt; internal static void ResetAutoRefresh() { _autoRefreshDone = false; _nextAutoRefreshAttemptAt = 0f; } internal static void TryAutoRefreshReferenceConfigurationFile() { if (_autoRefreshDone || !BossRulesPlugin.IsSourceOfTruth) { return; } float realtimeSinceStartup = Time.realtimeSinceStartup; if (realtimeSinceStartup < _nextAutoRefreshAttemptAt) { return; } if ((Object)(object)ZoneSystem.instance == (Object)null) { _nextAutoRefreshAttemptAt = realtimeSinceStartup + 1f; return; } if (ZoneSystem.instance.m_locations == null || ZoneSystem.instance.m_locations.Count == 0) { _nextAutoRefreshAttemptAt = realtimeSinceStartup + 1f; return; } try { int entryCount; string content = BuildReferenceConfigurationContent(out entryCount); if (entryCount == 0) { _nextAutoRefreshAttemptAt = realtimeSinceStartup + 1f; return; } WriteReferenceConfigurationFile(content); _autoRefreshDone = true; } catch (Exception exception) { _nextAutoRefreshAttemptAt = Time.realtimeSinceStartup + 5f; BossRulesPlugin.BossRulesLogger.LogWarning((object)$"Failed to update altar reference configuration at {BossRulesPlugin.AltarReferenceYamlFilePath}. {FormatException(exception)} Retrying in {5f:0.#}s."); } } private static string FormatException(Exception exception) { Exception ex = exception; while (ex is TargetInvocationException && ex.InnerException != null) { ex = ex.InnerException; } if (ex != exception) { return exception.GetType().Name + ": " + exception.Message + " Inner " + ex.GetType().Name + ": " + ex.Message + "."; } return exception.GetType().Name + ": " + exception.Message + "."; } private static string BuildReferenceConfigurationContent(out int entryCount) { List list = CaptureReferenceEntries().ToList(); entryCount = list.Count; StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine("# BossRules altar reference"); stringBuilder.AppendLine("# Generated from loaded ZoneSystem location prefabs."); stringBuilder.AppendLine("# Copy rows to BossRules.altar.yml to override."); stringBuilder.AppendLine("# This file is overwritten automatically."); stringBuilder.Append(SerializeReferenceEntries(list)); return stringBuilder.ToString(); } private static string SerializeReferenceEntries(IReadOnlyList entries) { if (entries.Count == 0) { return "[]" + Environment.NewLine; } StringBuilder stringBuilder = new StringBuilder(); foreach (AltarReferenceEntry item in entries.OrderBy((AltarReferenceEntry entry) => entry.Prefab, StringComparer.OrdinalIgnoreCase)) { stringBuilder.AppendLine(SerializeReferenceEntry(item)); } return stringBuilder.ToString(); } private static string SerializeReferenceEntry(AltarReferenceEntry entry) { string text = Serializer.Serialize(new AltarReferenceEntry[1] { entry }).TrimEnd('\r', '\n'); if (!RequiresQuotedPrefabScalar(entry.Prefab)) { return text; } int num = text.IndexOfAny(new char[2] { '\r', '\n' }); string text2 = ((num >= 0) ? text.Substring(num) : ""); string text3 = EscapeDoubleQuotedYamlScalar(entry.Prefab); return "- prefab: \"" + text3 + "\"" + text2; } private static bool RequiresQuotedPrefabScalar(string prefab) { return (prefab ?? "").IndexOf(':') >= 0; } private static string EscapeDoubleQuotedYamlScalar(string value) { return (value ?? "").Replace("\\", "\\\\").Replace("\"", "\\\""); } private static List CaptureReferenceEntries() { List list = new List(); HashSet capturedPrefabs = new HashSet(StringComparer.OrdinalIgnoreCase); DuplicateComponentWarnings.Clear(); foreach (ZoneLocation location in ZoneSystem.instance.m_locations) { if (TryCaptureReferenceEntry(location, capturedPrefabs, out AltarReferenceEntry entry) && entry != null) { list.Add(entry); } } return list; } private static bool TryCaptureReferenceEntry(ZoneLocation location, HashSet capturedPrefabs, out AltarReferenceEntry? entry) { //IL_003b: Unknown result type (might be due to invalid IL or missing references) entry = null; if (location == null || !location.m_prefab.IsValid) { return false; } string zoneLocationPrefabName = AltarLocationResolver.GetZoneLocationPrefabName(location); if (zoneLocationPrefabName.Length == 0 || !capturedPrefabs.Add(zoneLocationPrefabName)) { return false; } location.m_prefab.Load(); GameObject rootPrefab = location.m_prefab.Asset; if ((Object)(object)rootPrefab == (Object)null) { return false; } OfferingBowl[] componentsInChildren = rootPrefab.GetComponentsInChildren(true); ItemStand[] componentsInChildren2 = rootPrefab.GetComponentsInChildren(true); if (componentsInChildren.Length == 0 && componentsInChildren2.Length == 0) { return false; } WarnDuplicateComponent(zoneLocationPrefabName, "OfferingBowl", componentsInChildren.Length); entry = new AltarReferenceEntry { Prefab = zoneLocationPrefabName, OfferingBowl = ((componentsInChildren.Length != 0) ? ConvertReferenceOfferingBowl(AltarRuntime.CaptureOfferingBowlSnapshot(componentsInChildren[0])) : null), ItemStands = ((componentsInChildren2.Length != 0) ? (from itemStand in componentsInChildren2 where (Object)(object)itemStand != (Object)null select ConvertReferenceItemStand(rootPrefab.transform, itemStand)).OrderBy((AltarReferenceItemStandDefinition itemStand) => itemStand.Path, StringComparer.Ordinal).ToList() : null) }; if (entry.OfferingBowl == null) { List itemStands = entry.ItemStands; if (itemStands != null) { return itemStands.Count > 0; } return false; } return true; } private static AltarOfferingBowlDefinition ConvertReferenceOfferingBowl(OfferingBowlSnapshot snapshot) { return new AltarOfferingBowlDefinition { BossItem = ((snapshot.BossItem.Length == 0) ? null : snapshot.BossItem), BossItems = ((snapshot.BossItems == 1) ? ((int?)null) : new int?(snapshot.BossItems)), BossPrefab = ((snapshot.BossPrefab.Length == 0) ? null : snapshot.BossPrefab), ItemPrefab = ((snapshot.ItemPrefab.Length == 0) ? null : snapshot.ItemPrefab), SetGlobalKey = (string.IsNullOrWhiteSpace(snapshot.SetGlobalKey) ? null : snapshot.SetGlobalKey), RenderSpawnAreaGizmos = (snapshot.RenderSpawnAreaGizmos ? new bool?(true) : ((bool?)null)), AlertOnSpawn = (snapshot.AlertOnSpawn ? new bool?(true) : ((bool?)null)), SpawnBossDelay = (IsReferenceDefault(snapshot.SpawnBossDelay, 5f) ? ((float?)null) : new float?(snapshot.SpawnBossDelay)), SpawnBossDistance = RangeFormatting.FromReference(snapshot.SpawnBossMinDistance, snapshot.SpawnBossMaxDistance, 0f, 40f), SpawnBossMaxYDistance = (IsReferenceDefault(snapshot.SpawnBossMaxYDistance, 9999f) ? ((float?)null) : new float?(snapshot.SpawnBossMaxYDistance)), GetSolidHeightMargin = ((snapshot.GetSolidHeightMargin == 1000) ? ((int?)null) : new int?(snapshot.GetSolidHeightMargin)), EnableSolidHeightCheck = (snapshot.EnableSolidHeightCheck ? ((bool?)null) : new bool?(false)), SpawnPointClearingRadius = (IsReferenceDefault(snapshot.SpawnPointClearingRadius, 0f) ? ((float?)null) : new float?(snapshot.SpawnPointClearingRadius)), SpawnYOffset = (IsReferenceDefault(snapshot.SpawnYOffset, 1f) ? ((float?)null) : new float?(snapshot.SpawnYOffset)), UseItemStands = (snapshot.UseItemStands ? new bool?(true) : ((bool?)null)), ItemStandPrefix = (string.IsNullOrWhiteSpace(snapshot.ItemStandPrefix) ? null : snapshot.ItemStandPrefix), ItemStandMaxRange = (IsReferenceDefault(snapshot.ItemStandMaxRange, 20f) ? ((float?)null) : new float?(snapshot.ItemStandMaxRange)), RespawnMinutes = null }; } private static AltarReferenceItemStandDefinition ConvertReferenceItemStand(Transform root, ItemStand itemStand) { //IL_0071: Unknown result type (might be due to invalid IL or missing references) ItemStandSnapshot itemStandSnapshot = AltarRuntime.CaptureItemStandSnapshot(itemStand); return new AltarReferenceItemStandDefinition { Path = AltarRuntime.GetRelativePath(root, ((Component)itemStand).transform), CanBeRemoved = (itemStandSnapshot.CanBeRemoved ? ((bool?)null) : new bool?(false)), AutoAttach = (itemStandSnapshot.AutoAttach ? new bool?(true) : ((bool?)null)), OrientationType = ((string.IsNullOrWhiteSpace(itemStandSnapshot.OrientationType) || itemStandSnapshot.OrientationType == ((object)(Orientation)1/*cast due to .constrained prefix*/).ToString()) ? null : itemStandSnapshot.OrientationType), SupportedTypes = ((itemStandSnapshot.SupportedTypes.Count == 0) ? null : new FlowStringListDefinition(itemStandSnapshot.SupportedTypes)), SupportedItems = ((itemStandSnapshot.SupportedItems.Count == 0) ? null : new FlowStringListDefinition(itemStandSnapshot.SupportedItems)), UnsupportedItems = ((itemStandSnapshot.UnsupportedItems.Count == 0) ? null : new FlowStringListDefinition(itemStandSnapshot.UnsupportedItems)), PowerActivationDelay = (IsReferenceDefault(itemStandSnapshot.PowerActivationDelay, 2f) ? ((float?)null) : new float?(itemStandSnapshot.PowerActivationDelay)), GuardianPower = (string.IsNullOrWhiteSpace(itemStandSnapshot.GuardianPower) ? null : itemStandSnapshot.GuardianPower) }; } private static bool IsReferenceDefault(float actual, float expected) { return Math.Abs(actual - expected) < 0.0001f; } private static void WarnDuplicateComponent(string prefabName, string componentName, int count) { if (count > 1) { string item = prefabName + "@" + componentName; if (DuplicateComponentWarnings.Add(item)) { BossRulesPlugin.BossRulesLogger.LogWarning((object)("Location prefab '" + prefabName + "' has multiple " + componentName + " components. The first one will be used for BossRules.altar.yml.")); } } } private static void WriteReferenceConfigurationFile(string content) { string altarReferenceYamlFilePath = BossRulesPlugin.AltarReferenceYamlFilePath; if (!string.Equals(File.Exists(altarReferenceYamlFilePath) ? File.ReadAllText(altarReferenceYamlFilePath) : "", content, StringComparison.Ordinal)) { File.WriteAllText(altarReferenceYamlFilePath, content, Encoding.UTF8); BossRulesPlugin.BossRulesLogger.LogInfo((object)("Updated altar reference configuration at " + altarReferenceYamlFilePath + ".")); } } } internal sealed class BossRuleConfigurationSection { [YamlMember(Order = 1)] public BossDespawnConfigurationDefinition? Despawn { get; set; } [YamlMember(Order = 2)] public BossTamedPressureDefinition? BossTamedPressure { get; set; } [YamlMember(Order = 3)] public BossRuleLocalizationDefinition? Localization { get; set; } } internal sealed class BossDespawnConfigurationDefinition { [YamlMember(Order = 1)] public string? Defaults { get; set; } [YamlMember(Order = 2)] public List? Rules { get; set; } } internal sealed class BossRuleLocalizationDefinition { [YamlMember(Order = 1)] public string? MessageDespawnStart { get; set; } [YamlMember(Order = 2)] public string? MessageDespawnReminder { get; set; } [YamlMember(Order = 3)] public string? MessageDespawnCanceled { get; set; } [YamlMember(Order = 4)] public string? MessageBossTamedPressure { get; set; } [YamlMember(Order = 5)] public string? MessageForsakenPowerRotate { get; set; } } internal sealed class BossDespawnDefinition { internal string Prefab { get; } public float? DespawnRange { get; set; } public float? DespawnDelay { get; set; } public bool? Refunds { get; set; } internal BossDespawnDefinition(string prefab, float? despawnRange, float? despawnDelay, bool? refunds) { Prefab = prefab; DespawnRange = despawnRange; DespawnDelay = despawnDelay; Refunds = refunds; } } internal sealed class BossRuleConfigurationState { internal static BossRuleConfigurationState Empty => new BossRuleConfigurationState(); internal float DefaultDespawnRange { get; set; } = 64f; internal float DefaultDespawnDelaySeconds { get; set; } = 90f; internal List DespawnRules { get; } = new List(); internal List BossTamedPressureRules { get; } = new List(); internal List ForsakenPowers { get; } = new List(); internal string? MessageDespawnStart { get; set; } internal string? MessageDespawnReminder { get; set; } internal string? MessageDespawnCanceled { get; set; } internal string? MessageBossTamedPressure { get; set; } internal string? MessageForsakenPowerRotate { get; set; } } internal sealed class BossTamedPressureDefinition { [YamlMember(Order = 1)] public List? BossPrefabs { get; set; } [YamlMember(Order = 2)] public List? ExcludedBossPrefabs { get; set; } [YamlMember(Order = 3)] public BossTamedPressureTargetsDefinition? Targets { get; set; } [YamlMember(Order = 4)] public BossTamedPressurePressureDefinition? Pressure { get; set; } } internal sealed class BossTamedPressureTargetsDefinition { [YamlMember(Order = 1)] public float? Range { get; set; } [YamlMember(Order = 2)] public int? MaxPerBoss { get; set; } [YamlMember(Order = 3)] public List? ExcludedTamedPrefabs { get; set; } [YamlMember(Order = 4)] public List? ExtraPressuredPrefabs { get; set; } } internal sealed class BossTamedPressurePressureDefinition { [YamlMember(Order = 1)] public float? DamagePercentPerSecond { get; set; } [YamlMember(Order = 2)] public float? IncomingDamageMultiplier { get; set; } [YamlMember(Order = 3)] public float? OutgoingDamageMultiplier { get; set; } } internal static class BossRuleConfiguration { private static readonly IDeserializer Deserializer = new DeserializerBuilder().WithNamingConvention(CamelCaseNamingConvention.Instance).Build(); internal static bool TryParse(string yaml, string source, out BossRuleConfigurationState state) { state = BossRuleConfigurationState.Empty; try { BossRuleConfigurationSection bossRuleConfigurationSection = (string.IsNullOrWhiteSpace(yaml) ? new BossRuleConfigurationSection() : Deserializer.Deserialize(yaml)); state = Normalize(bossRuleConfigurationSection ?? new BossRuleConfigurationSection()); BossRulesPlugin.BossRulesLogger.LogInfo((object)$"Loaded boss rules YAML from {source}: {state.DespawnRules.Count} despawn entries, {state.BossTamedPressureRules.Count} boss tamed pressure entries."); return true; } catch (Exception ex) { BossRulesPlugin.BossRulesLogger.LogError((object)("Rejected boss rules YAML from " + source + ". Keeping the previous configuration. " + ex.GetType().Name + ": " + ex.Message)); return false; } } private static BossRuleConfigurationState Normalize(BossRuleConfigurationSection section) { BossRuleConfigurationState bossRuleConfigurationState = new BossRuleConfigurationState(); (bossRuleConfigurationState.DefaultDespawnRange, bossRuleConfigurationState.DefaultDespawnDelaySeconds) = ParseDespawnDefaults(section.Despawn?.Defaults); foreach (string item in section.Despawn?.Rules ?? new List()) { bossRuleConfigurationState.DespawnRules.Add(ParseDespawnRule(item)); } if (section.BossTamedPressure != null) { NormalizeBossTamedPressure(section.BossTamedPressure); bossRuleConfigurationState.BossTamedPressureRules.Add(section.BossTamedPressure); } BossRuleLocalizationDefinition localization = section.Localization; if (localization != null && localization.MessageDespawnStart != null) { bossRuleConfigurationState.MessageDespawnStart = localization.MessageDespawnStart.Trim(); } if (localization != null && localization.MessageDespawnReminder != null) { bossRuleConfigurationState.MessageDespawnReminder = localization.MessageDespawnReminder.Trim(); } if (localization != null && localization.MessageDespawnCanceled != null) { bossRuleConfigurationState.MessageDespawnCanceled = localization.MessageDespawnCanceled.Trim(); } if (localization != null && localization.MessageBossTamedPressure != null) { bossRuleConfigurationState.MessageBossTamedPressure = localization.MessageBossTamedPressure.Trim(); } if (localization != null && localization.MessageForsakenPowerRotate != null) { bossRuleConfigurationState.MessageForsakenPowerRotate = localization.MessageForsakenPowerRotate.Trim(); } return bossRuleConfigurationState; } private static (float Range, float DelaySeconds) ParseDespawnDefaults(string? rawDefaults) { if (string.IsNullOrWhiteSpace(rawDefaults)) { return (Range: 64f, DelaySeconds: 90f); } string text = rawDefaults.Trim(); string[] array = text.Split(new char[1] { ',' }); if (array.Length > 2) { throw new FormatException("despawn.defaults '" + text + "' has too many values. Expected 'range, delaySeconds'."); } float item = ParseDefaultFloat(array, 0, "range", text, 64f); float item2 = ParseDefaultFloat(array, 1, "delaySeconds", text, 90f); return (Range: item, DelaySeconds: item2); } private static float ParseDefaultFloat(string[] parts, int index, string fieldName, string rawDefaults, float fallback) { if (parts.Length <= index) { return fallback; } string text = parts[index].Trim(); if (text.Length == 0) { return fallback; } if (float.TryParse(text, NumberStyles.Float | NumberStyles.AllowThousands, CultureInfo.InvariantCulture, out var result)) { return result; } throw new FormatException("despawn.defaults '" + rawDefaults + "' has invalid " + fieldName + " value '" + text + "'."); } private static BossDespawnDefinition ParseDespawnRule(string rawRule) { string text = (rawRule ?? "").Trim(); if (text.Length == 0) { throw new FormatException("Empty despawn row. Expected '- prefab, despawnRange, despawnDelay, refunds'."); } string[] array = text.Split(new char[1] { ',' }); if (array.Length > 4) { throw new FormatException("Despawn row '" + text + "' has too many values. Expected '- prefab, despawnRange, despawnDelay, refunds'."); } string text2 = array[0].Trim(); if (text2.Length == 0) { throw new FormatException("Despawn row '" + text + "' has an empty prefab name."); } return new BossDespawnDefinition(text2, ParseOptionalFloat(array, 1, "despawnRange", text), ParseOptionalFloat(array, 2, "despawnDelay", text), ParseOptionalBool(array, 3, "refunds", text)); } private static float? ParseOptionalFloat(string[] parts, int index, string fieldName, string rawRule) { if (parts.Length <= index) { return null; } string text = parts[index].Trim(); if (text.Length == 0) { return null; } if (float.TryParse(text, NumberStyles.Float | NumberStyles.AllowThousands, CultureInfo.InvariantCulture, out var result)) { return result; } throw new FormatException("Despawn row '" + rawRule + "' has invalid " + fieldName + " value '" + text + "'."); } private static bool? ParseOptionalBool(string[] parts, int index, string fieldName, string rawRule) { if (parts.Length <= index) { return null; } string text = parts[index].Trim(); if (text.Length == 0) { return null; } if (bool.TryParse(text, out var result)) { return result; } throw new FormatException("Despawn row '" + rawRule + "' has invalid " + fieldName + " value '" + text + "'. Use true or false."); } private static void NormalizeBossTamedPressure(BossTamedPressureDefinition? definition) { if (definition != null) { definition.BossPrefabs = NormalizeStringList(definition.BossPrefabs); definition.ExcludedBossPrefabs = NormalizeStringList(definition.ExcludedBossPrefabs); if (definition.Targets != null) { definition.Targets.ExcludedTamedPrefabs = NormalizeStringList(definition.Targets.ExcludedTamedPrefabs); definition.Targets.ExtraPressuredPrefabs = NormalizeStringList(definition.Targets.ExtraPressuredPrefabs); } } } private static List? NormalizeStringList(List? values) { List list = (from value in values?.Select((string value) => (value ?? "").Trim()) where value.Length > 0 select value).Distinct(StringComparer.OrdinalIgnoreCase).ToList(); if (list == null || list.Count <= 0) { return null; } return list; } private static string? NormalizeOptionalString(string? value) { string text = (value ?? "").Trim(); if (text.Length <= 0) { return null; } return text; } } internal static class BossRuleConfigurationFiles { internal static void EnsureDefaultFile() { EnsureTextFile(BossRulesPlugin.RulesYamlFilePath, BuildDefaultRulesYaml()); } private static void EnsureTextFile(string path, string content) { if (!File.Exists(path)) { File.WriteAllText(path, content); BossRulesPlugin.BossRulesLogger.LogInfo((object)("Created " + path + ".")); } } private static string BuildDefaultRulesYaml() { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine("# BossRules runtime rules"); stringBuilder.AppendLine("#"); stringBuilder.AppendLine("despawn:"); stringBuilder.AppendLine(" defaults: 64, 90 # default despawnRange, despawnDelaySeconds."); stringBuilder.AppendLine(" rules:"); stringBuilder.AppendLine(" # - prefab, despawnRange, despawnDelay, refunds"); stringBuilder.AppendLine(" # Empty or omitted despawnRange/despawnDelay uses despawn.defaults."); stringBuilder.AppendLine(" # despawnRange: 0 disables despawn for that prefab."); stringBuilder.AppendLine(" # refunds omitted or empty: true. Use false to disable altar offering refunds."); stringBuilder.AppendLine(" - Fader, 64, 90, true # Boss prefabs are auto-detected, but non-boss Character prefabs can also be listed here for despawn rules."); stringBuilder.AppendLine(); stringBuilder.AppendLine("bossTamedPressure:"); stringBuilder.AppendLine(" bossPrefabs: [Eikthyr] # Extra source boss prefabs added to the auto-detected boss set"); stringBuilder.AppendLine(" excludedBossPrefabs: [] # Boss prefabs to ignore from auto-detected and bossPrefabs sources"); stringBuilder.AppendLine(" targets:"); stringBuilder.AppendLine(" range: 32 # Clamp: 0~128. Horizontal XZ range around each boss"); stringBuilder.AppendLine(" maxPerBoss: 4 # Clamp: 1~128. Maximum pressured targets per boss per scan"); stringBuilder.AppendLine(" excludedTamedPrefabs: [] # Tamed MonsterAI prefabs excluded from the default pressured target set"); stringBuilder.AppendLine(" extraPressuredPrefabs: [] # Character prefabs pressured even when not tamed"); stringBuilder.AppendLine(" pressure:"); stringBuilder.AppendLine(" damagePercentPerSecond: 0.01 # Clamp: 0~1. 0.01 = 1% of max health per second"); stringBuilder.AppendLine(" incomingDamageMultiplier: 1.25 # Clamp: 0~10. Multiplies damage received while affected"); stringBuilder.AppendLine(" outgoingDamageMultiplier: 0.75 # Clamp: 0~10. Multiplies damage dealt while affected"); stringBuilder.AppendLine(); stringBuilder.AppendLine("localization:"); stringBuilder.AppendLine(" messageDespawnStart: \"{name} will despawn in {seconds}s unless someone returns.\""); stringBuilder.AppendLine(" messageDespawnReminder: \"{name} will despawn in {seconds}s.\""); stringBuilder.AppendLine(" messageDespawnCanceled: \"{name} despawn canceled.\""); stringBuilder.AppendLine(" messageBossTamedPressure: \"Tamed creatures near a boss are weakened.\""); stringBuilder.AppendLine(" messageForsakenPowerRotate: \"Rotate\""); return stringBuilder.ToString(); } } internal static class BossRulesConsoleCommands { [CompilerGenerated] private static class <>O { public static ConsoleEvent <0>__InspectRuntimeTarget; public static ConsoleOptionsFetcher <1>__GetInspectTabOptions; public static ConsoleEvent <2>__HandleBossStoneCommand; public static ConsoleOptionsFetcher <3>__GetBossStoneTabOptions; } private const string InspectCommandName = "bossrules:inspect"; private const string BossStoneCommandName = "bossrules:bossstone"; private static readonly List InspectTabOptions = new List { "bossstone" }; private static readonly List BossStoneTabOptions = new List { "reset" }; 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 //IL_0076: 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_0081: Expected O, but got Unknown //IL_00a4: 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_009b: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Expected O, but got Unknown if (!_registered) { _registered = true; object obj = <>O.<0>__InspectRuntimeTarget; if (obj == null) { ConsoleEvent val = InspectRuntimeTarget; <>O.<0>__InspectRuntimeTarget = val; obj = (object)val; } object obj2 = <>O.<1>__GetInspectTabOptions; if (obj2 == null) { ConsoleOptionsFetcher val2 = GetInspectTabOptions; <>O.<1>__GetInspectTabOptions = val2; obj2 = (object)val2; } new ConsoleCommand("bossrules:inspect", "Inspect the current hovered/aimed BossRules runtime target. Currently supports: bossstone.", (ConsoleEvent)obj, false, false, false, false, false, (ConsoleOptionsFetcher)obj2, false, false, false); object obj3 = <>O.<2>__HandleBossStoneCommand; if (obj3 == null) { ConsoleEvent val3 = HandleBossStoneCommand; <>O.<2>__HandleBossStoneCommand = val3; obj3 = (object)val3; } object obj4 = <>O.<3>__GetBossStoneTabOptions; if (obj4 == null) { ConsoleOptionsFetcher val4 = GetBossStoneTabOptions; <>O.<3>__GetBossStoneTabOptions = val4; obj4 = (object)val4; } new ConsoleCommand("bossrules:bossstone", "Reset per-player boss stone state. Syntax: bossrules:bossstone reset ", (ConsoleEvent)obj3, true, false, false, false, false, (ConsoleOptionsFetcher)obj4, false, false, true); } } private static List GetInspectTabOptions() { return InspectTabOptions; } private static List GetBossStoneTabOptions() { return BossStoneTabOptions; } private static void InspectRuntimeTarget(ConsoleEventArgs args) { string[] lines; string error; if (((args.Length >= 2) ? (args[1] ?? "").Trim().ToLowerInvariant() : "") != "bossstone") { Terminal context = args.Context; if (context != null) { context.AddString("Syntax: bossrules:inspect bossstone"); } } else if (BossStonePerPlayerRuntime.TryInspectCurrentTarget(out lines, out error)) { string[] array = lines; foreach (string text in array) { Terminal context2 = args.Context; if (context2 != null) { context2.AddString(text); } } } else { Terminal context3 = args.Context; if (context3 != null) { context3.AddString(error); } } } private static void HandleBossStoneCommand(ConsoleEventArgs args) { if (((args.Length >= 2) ? (args[1] ?? "").Trim().ToLowerInvariant() : "") != "reset") { Terminal context = args.Context; if (context != null) { context.AddString("Syntax: bossrules:bossstone reset "); } return; } BossStonePerPlayerRuntime.TryRequestReset((args.FullLine.Length > "bossrules:bossstone reset".Length) ? args.FullLine.Substring("bossrules:bossstone reset".Length).Trim() : "", out string message); Terminal context2 = args.Context; if (context2 != null) { context2.AddString(message); } } } internal static class AltarRuntime { private sealed class AuthoredItemStandSlotTemplate { public string Path { get; set; } = ""; public Vector3 OfferingBowlLocalOffset { get; set; } } internal enum OfferingBowlBlockReason { None, SameBossNearby, RespawnCooldownActive } internal readonly struct OfferingBowlBlockResult { internal static OfferingBowlBlockResult None => default(OfferingBowlBlockResult); public bool Blocked { get; } public OfferingBowlBlockReason Reason { get; } public OfferingBowlBlockResult(bool blocked, OfferingBowlBlockReason reason) { Blocked = blocked; Reason = reason; } } private sealed class PendingAltarBossSpawn { public string BossPrefabName { get; set; } = ""; public int BossPrefabHash { get; set; } public Vector3 SpawnPoint { get; set; } public Vector3 RefundPoint { get; set; } public string RefundPayload { get; set; } = ""; public float ExpiresAt { get; set; } } private static readonly object Sync = new object(); private static readonly int OfferingBowlLastUseTicksKey = StringExtensionMethods.GetStableHashCode("BossRules.offering_bowl_last_use_ticks"); private static readonly Dictionary> ActiveEntriesByPrefab = new Dictionary>(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary RegisteredLocationPrefabs = new Dictionary(); private static readonly Dictionary> AuthoredItemStandSlotsByPrefab = new Dictionary>(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary LooseItemStandAuthoredPathsByInstance = new Dictionary(); private static bool _pendingGameDataReapply; private static bool _loggedPendingGameDataWait; private static readonly int AltarSummonKey = StringExtensionMethods.GetStableHashCode("BossRules.altar_summon"); private static readonly int AltarRefundsKey = StringExtensionMethods.GetStableHashCode("BossRules.altar_refunds"); private static readonly int AltarRefundPointKey = StringExtensionMethods.GetStableHashCode("BossRules.altar_refund_point"); private const float AltarSpawnMarkerMaxDistance = 128f; private const float AltarSpawnMarkerMaxDistanceSquared = 16384f; private const float AltarSpawnMarkerRetrySeconds = 5f; private const float AltarSpawnMarkerRetryIntervalSeconds = 0.25f; private static readonly List PendingAltarBossSpawns = new List(); private static readonly List PendingAltarBossSpawnRemovals = new List(); private static float _nextAltarSpawnMarkerRetryAt; internal static void Reload(IReadOnlyList entries) { lock (Sync) { ActiveEntriesByPrefab.Clear(); AuthoredItemStandSlotsByPrefab.Clear(); LooseItemStandAuthoredPathsByInstance.Clear(); AltarItemStandHoverInfoFormatter.ClearRuntimeCaches(); _pendingGameDataReapply = true; _loggedPendingGameDataWait = false; foreach (AltarConfigurationEntry entry in entries) { if (entry.Enabled && HasOverride(entry)) { if (!ActiveEntriesByPrefab.TryGetValue(entry.Prefab, out List value)) { value = new List(); ActiveEntriesByPrefab[entry.Prefab] = value; } value.Add(entry); } } BossRulesDebugLog.Client($"Altar reload entries={entries.Count} activePrefabs={ActiveEntriesByPrefab.Count} gameDataReady={IsGameDataReady()} registeredLocations={RegisteredLocationPrefabs.Count}."); ReapplyRegisteredLocationsLocked(); } } internal static void Shutdown() { lock (Sync) { foreach (KeyValuePair item in RegisteredLocationPrefabs.ToList()) { Location key = item.Key; if ((Object)(object)key != (Object)null) { RestoreRoot(((Component)key).transform); } } RegisteredLocationPrefabs.Clear(); ActiveEntriesByPrefab.Clear(); AuthoredItemStandSlotsByPrefab.Clear(); LooseItemStandAuthoredPathsByInstance.Clear(); AltarItemStandHoverInfoFormatter.ClearRuntimeCaches(); _pendingGameDataReapply = false; _loggedPendingGameDataWait = false; } } internal static void ProcessDeferredReapply() { lock (Sync) { if (!_pendingGameDataReapply) { return; } if (!IsGameDataReady()) { if (!_loggedPendingGameDataWait) { _loggedPendingGameDataWait = true; BossRulesDebugLog.Client("Altar deferred reapply waiting for game data. " + DescribeGameDataState()); } } else { _pendingGameDataReapply = false; _loggedPendingGameDataWait = false; BossRulesDebugLog.Client($"Altar deferred reapply running. {DescribeGameDataState()} registeredLocations={RegisteredLocationPrefabs.Count} activePrefabs={ActiveEntriesByPrefab.Count}."); ReapplyRegisteredLocationsLocked(); ReapplyLoadedLooseOfferingBowlsLocked(); ReapplyLoadedLooseItemStandsLocked(); } } } internal static bool HasConfiguredPrefab(string prefabName) { lock (Sync) { return ActiveEntriesByPrefab.ContainsKey((prefabName ?? "").Trim()); } } internal static void RegisterLocation(Location? location) { if ((Object)(object)location == (Object)null) { return; } lock (Sync) { if (!AltarLocationResolver.TryResolveLocationPrefabName(location, out string prefabName)) { BossRulesDebugLog.Client("Altar register location skipped: prefab unresolved location=" + ((Object)location).name + "."); return; } RegisteredLocationPrefabs[location] = prefabName; BossRulesDebugLog.Client("Altar registered location prefab=" + prefabName + " location=" + ((Object)location).name + "."); ReconcileRootLocked(((Component)location).transform, prefabName); } } internal static void UnregisterLocation(Location? location) { if ((Object)(object)location == (Object)null) { return; } lock (Sync) { RegisteredLocationPrefabs.Remove(location); } } internal static void ReconcileSpawnedLocationRoot(GameObject? rootObject, string prefabName) { if ((Object)(object)rootObject == (Object)null) { return; } string text = (prefabName ?? "").Trim(); if (text.Length == 0) { return; } lock (Sync) { RefreshRegisteredLocationPrefabsLocked(rootObject.transform, text); ReconcileRootLocked(rootObject.transform, text); } } private static void RefreshRegisteredLocationPrefabsLocked(Transform root, string prefabName) { Location[] componentsInChildren = ((Component)root).GetComponentsInChildren(true); Location[] array = componentsInChildren; foreach (Location val in array) { if ((Object)(object)val != (Object)null) { RegisteredLocationPrefabs[val] = prefabName; } } if (componentsInChildren.Length != 0) { BossRulesDebugLog.Client($"Altar refreshed spawned location prefab cache prefab={prefabName} root={((Object)root).name} locations={componentsInChildren.Length}."); } } internal static void ReconcileLooseOfferingBowl(OfferingBowl? offeringBowl) { if ((Object)(object)offeringBowl == (Object)null || (Object)(object)((Component)offeringBowl).GetComponentInParent(true) != (Object)null) { return; } lock (Sync) { if (!AltarItemStandHoverInfoFormatter.TryResolveOfferingBowlContext(offeringBowl, out string locationPrefab, out Transform root)) { BossRulesDebugLog.Client("Altar loose offering bowl skipped: context unresolved bowl=" + ((Object)offeringBowl).name + "."); return; } BossRulesDebugLog.Client("Altar loose offering bowl context prefab=" + locationPrefab + " root=" + ((Object)root).name + " bowl=" + ((Object)offeringBowl).name + "."); ReconcileRootLocked(root, locationPrefab); } } internal static void ReconcileLooseItemStand(ItemStand? itemStand) { lock (Sync) { ReconcileLooseItemStandLocked(itemStand); } } internal static OfferingBowlBlockResult EvaluateOfferingBowlBlock(OfferingBowl? offeringBowl) { //IL_003e: Unknown result type (might be due to invalid IL or missing references) lock (Sync) { if ((Object)(object)offeringBowl == (Object)null || (Object)(object)ZNet.instance == (Object)null) { return OfferingBowlBlockResult.None; } if (BossRulesManager.ShouldBlockConfiguredSameBossSpawn(offeringBowl.m_bossPrefab, ((Component)offeringBowl).transform.position)) { return new OfferingBowlBlockResult(blocked: true, OfferingBowlBlockReason.SameBossNearby); } OfferingBowlRuntimeState component = ((Component)offeringBowl).GetComponent(); if ((Object)(object)component == (Object)null || component.RespawnMinutes <= 0f) { return OfferingBowlBlockResult.None; } long offeringBowlLastUseTicks = GetOfferingBowlLastUseTicks(offeringBowl, component); if (offeringBowlLastUseTicks <= 0) { return OfferingBowlBlockResult.None; } return ((ZNet.instance.GetTime() - new DateTime(offeringBowlLastUseTicks)).TotalMinutes >= (double)component.RespawnMinutes) ? OfferingBowlBlockResult.None : new OfferingBowlBlockResult(blocked: true, OfferingBowlBlockReason.RespawnCooldownActive); } } internal static void NotifyOfferingBowlBlocked(OfferingBowl offeringBowl, Humanoid? user, OfferingBowlBlockResult result) { if (!((Object)(object)offeringBowl == (Object)null) && !((Object)(object)user == (Object)null) && result.Blocked) { ((Character)user).Message((MessageType)2, Localization.instance.Localize(offeringBowl.m_cantOfferText), 0, (Sprite)null); } } internal static void MarkOfferingBowlUsed(OfferingBowl? offeringBowl) { lock (Sync) { if ((Object)(object)offeringBowl == (Object)null || (Object)(object)ZNet.instance == (Object)null) { return; } OfferingBowlRuntimeState component = ((Component)offeringBowl).GetComponent(); if ((Object)(object)component == (Object)null || component.RespawnMinutes <= 0f) { return; } long num = (component.LocalLastUseTicks = ZNet.instance.GetTime().Ticks); ZNetView componentInParent = ((Component)offeringBowl).GetComponentInParent(); if (!((Object)(object)componentInParent == (Object)null) && componentInParent.IsValid()) { if (!componentInParent.IsOwner()) { componentInParent.ClaimOwnership(); } if (componentInParent.IsOwner()) { componentInParent.GetZDO().Set(OfferingBowlLastUseTicksKey, num); } } } } internal static void BeginOfferingBowlBossSpawnAttempt(OfferingBowl? offeringBowl, Vector3 spawnPoint) { //IL_003a: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)ZNet.instance == (Object)null || (Object)(object)offeringBowl?.m_bossPrefab == (Object)null) { return; } lock (Sync) { string refundPayload = ConsumePreparedOfferingRefundPayload(offeringBowl); QueueOfferingBowlBossSpawnAttemptLocked(offeringBowl, spawnPoint, refundPayload, 0f, "started"); } } internal static void PrepareOfferingBowlRefundPayload(OfferingBowl? offeringBowl) { if ((Object)(object)ZNet.instance == (Object)null || (Object)(object)offeringBowl == (Object)null) { return; } lock (Sync) { string text = BuildOfferingRefundPayload(offeringBowl); GetOrAddOfferingBowlRuntimeState(offeringBowl).PendingRefundPayload = text; BossRulesDebugLog.Client($"Altar refund prepared altar='{((Object)offeringBowl).name}' useItemStands={offeringBowl.m_useItemStands} payload='{FormatRefundPayloadForLog(text)}'."); } } internal static void PrepareAndQueueOfferingBowlRefundPayload(OfferingBowl? offeringBowl, Vector3 spawnPoint) { //IL_006c: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)ZNet.instance == (Object)null || (Object)(object)offeringBowl?.m_bossPrefab == (Object)null) { return; } lock (Sync) { string text = BuildOfferingRefundPayload(offeringBowl); GetOrAddOfferingBowlRuntimeState(offeringBowl).PendingRefundPayload = text; BossRulesDebugLog.Client($"Altar refund prepared altar='{((Object)offeringBowl).name}' useItemStands={offeringBowl.m_useItemStands} payload='{FormatRefundPayloadForLog(text)}'."); QueueOfferingBowlBossSpawnAttemptLocked(offeringBowl, spawnPoint, text, Math.Max(0f, offeringBowl.m_spawnBossDelay), "queued"); } } internal static void FinalizeOfferingBowlBossSpawnAttempt(OfferingBowl? offeringBowl, Vector3 spawnPoint) { if ((Object)(object)ZNet.instance == (Object)null) { return; } lock (Sync) { TryMarkNearbyPendingAltarSummonsLocked(); } } internal static void TryMarkAltarSummonedCharacter(Character? character) { if ((Object)(object)ZNet.instance == (Object)null || (Object)(object)((character != null) ? ((Component)character).gameObject : null) == (Object)null) { return; } ZNetView component = ((Component)character).GetComponent(); if ((Object)(object)component == (Object)null || !component.IsValid()) { return; } lock (Sync) { TryMarkAltarSummonedCharacterLocked(character, component.GetZDO()); } } internal static string GetPrefabName(GameObject? gameObject) { if ((Object)(object)gameObject == (Object)null) { return ""; } ZNetView component = gameObject.GetComponent(); ZDO val = ((component != null) ? component.GetZDO() : null); if (val != null && (Object)(object)ZNetScene.instance != (Object)null) { GameObject prefab = ZNetScene.instance.GetPrefab(val.GetPrefab()); if ((Object)(object)prefab != (Object)null) { return ((Object)prefab).name; } } string prefabName = Utils.GetPrefabName(gameObject); if (!string.IsNullOrWhiteSpace(prefabName)) { return prefabName; } return TrimCloneSuffix(((Object)gameObject).name); } private static void ReapplyRegisteredLocationsLocked() { BossRulesDebugLog.Client($"Altar reapply registered locations count={RegisteredLocationPrefabs.Count}."); foreach (KeyValuePair item in RegisteredLocationPrefabs.ToList()) { Location key = item.Key; string value = item.Value; if ((Object)(object)key == (Object)null) { RegisteredLocationPrefabs.Remove(item.Key); } else { ReconcileRootLocked(((Component)key).transform, value); } } } private static void ReapplyLoadedLooseOfferingBowlsLocked() { int num = 0; int num2 = 0; int num3 = 0; OfferingBowl[] array = Object.FindObjectsByType((FindObjectsSortMode)0); foreach (OfferingBowl val in array) { num++; if (!((Object)(object)val == (Object)null) && !((Object)(object)((Component)val).GetComponentInParent(true) != (Object)null)) { if (AltarItemStandHoverInfoFormatter.TryResolveOfferingBowlContext(val, out string locationPrefab, out Transform root)) { num2++; ReconcileRootLocked(root, locationPrefab); } else { num3++; } } } BossRulesDebugLog.Client($"Altar reapply loose offering bowls scanned={num} applied={num2} unresolved={num3}."); } private static void ReapplyLoadedLooseItemStandsLocked() { int num = 0; int num2 = 0; ItemStand[] array = Object.FindObjectsByType((FindObjectsSortMode)0); foreach (ItemStand itemStand in array) { num++; if (ReconcileLooseItemStandLocked(itemStand)) { num2++; } } BossRulesDebugLog.Client($"Altar reapply loose item stands scanned={num} applied={num2}."); } private static bool ReconcileLooseItemStandLocked(ItemStand? itemStand) { if ((Object)(object)itemStand == (Object)null || (Object)(object)((Component)itemStand).GetComponentInParent(true) != (Object)null) { return false; } if (!AltarItemStandHoverInfoFormatter.TryGetRelevantOfferingBowl(itemStand, out OfferingBowl offeringBowl) || (Object)(object)offeringBowl == (Object)null) { BossRulesDebugLog.Client("Altar loose itemStand skipped: offeringBowl unresolved stand=" + ((Object)itemStand).name + "."); return false; } Location componentInParent = ((Component)offeringBowl).GetComponentInParent(true); if ((Object)(object)componentInParent != (Object)null) { if (!RegisteredLocationPrefabs.TryGetValue(componentInParent, out string value) && AltarLocationResolver.TryResolveLocationPrefabName(componentInParent, out value)) { RegisteredLocationPrefabs[componentInParent] = value; } if (string.IsNullOrWhiteSpace(value)) { BossRulesDebugLog.Client("Altar loose itemStand skipped: location prefab unresolved stand=" + ((Object)itemStand).name + " bowl=" + ((Object)offeringBowl).name + "."); return false; } BossRulesDebugLog.Client("Altar loose itemStand reapplying location prefab=" + value + " stand=" + ((Object)itemStand).name + " bowl=" + ((Object)offeringBowl).name + "."); ReconcileRootLocked(((Component)componentInParent).transform, value); return true; } if (AltarItemStandHoverInfoFormatter.TryResolveOfferingBowlContext(offeringBowl, out string locationPrefab, out Transform root)) { BossRulesDebugLog.Client("Altar loose itemStand reapplying loose root prefab=" + locationPrefab + " stand=" + ((Object)itemStand).name + " bowl=" + ((Object)offeringBowl).name + "."); ReconcileRootLocked(root, locationPrefab); return true; } BossRulesDebugLog.Client("Altar loose itemStand skipped: context unresolved stand=" + ((Object)itemStand).name + " bowl=" + ((Object)offeringBowl).name + "."); return false; } private static void ReconcileRootLocked(Transform? root, string prefabName) { if ((Object)(object)root == (Object)null) { return; } string text = (prefabName ?? "").Trim(); if (text.Length == 0) { return; } if (!IsGameDataReady()) { _pendingGameDataReapply = true; BossRulesDebugLog.Client("Altar reconcile deferred prefab=" + text + " root=" + ((Object)root).name + ". " + DescribeGameDataState()); return; } OfferingBowl[] componentsInChildren = ((Component)root).GetComponentsInChildren(true); ItemStand[] componentsInChildren2 = ((Component)root).GetComponentsInChildren(true); CaptureSnapshots(componentsInChildren, componentsInChildren2); RestoreComponents(componentsInChildren, componentsInChildren2); if (!BossRulesConfig.IsAltarRulesEnabled() || !ActiveEntriesByPrefab.TryGetValue(text, out List value)) { BossRulesDebugLog.Client($"Altar reconcile restore-only prefab={text} root={((Object)root).name} enabled={BossRulesConfig.IsAltarRulesEnabled()} configured={ActiveEntriesByPrefab.ContainsKey(text)} bowls={componentsInChildren.Length} childItemStands={componentsInChildren2.Length}."); return; } OfferingBowl val = ((IEnumerable)componentsInChildren).FirstOrDefault((Func)((OfferingBowl bowl) => (Object)(object)bowl != (Object)null)); Dictionary dictionary = BuildItemStandLookup(root, componentsInChildren2); BossRulesDebugLog.Client(string.Format("Altar reconcile applying prefab={0} root={1} entries={2} bowls={3} childItemStands={4} childPaths={5} offeringBowl={6}.", text, ((Object)root).name, value.Count, componentsInChildren.Length, componentsInChildren2.Length, dictionary.Count, ((Object)(object)val != (Object)null) ? ((Object)val).name : "")); foreach (AltarConfigurationEntry item in value) { if (item.OfferingBowl != null && (Object)(object)val != (Object)null) { ApplyOfferingBowl(val, item.OfferingBowl, text); } List itemStands = item.ItemStands; if (itemStands != null && itemStands.Count > 0) { List relevantItemStands = GetRelevantItemStands(val, componentsInChildren2); ApplyConfiguredItemStands(item.ItemStands, relevantItemStands, dictionary, text, root, val); } } } private static bool IsGameDataReady() { if ((Object)(object)ZoneSystem.instance != (Object)null) { List list = ObjectDB.instance?.m_items; if (list != null && list.Count > 0) { list = ZNetScene.instance?.m_prefabs; if (list != null) { return list.Count > 0; } return false; } } return false; } private static string DescribeGameDataState() { string arg = (((Object)(object)ZoneSystem.instance != (Object)null) ? "ready" : "null"); int num = ObjectDB.instance?.m_items?.Count ?? (-1); int num2 = ZNetScene.instance?.m_prefabs?.Count ?? (-1); return $"ZoneSystem={arg} ObjectDB.items={num} ZNetScene.prefabs={num2}"; } private static void CaptureSnapshots(IEnumerable offeringBowls, IEnumerable itemStands) { foreach (OfferingBowl offeringBowl in offeringBowls) { if (!((Object)(object)offeringBowl == (Object)null)) { OfferingBowlRuntimeState orAddOfferingBowlRuntimeState = GetOrAddOfferingBowlRuntimeState(offeringBowl); if (orAddOfferingBowlRuntimeState.Snapshot == null) { OfferingBowlSnapshot offeringBowlSnapshot = (orAddOfferingBowlRuntimeState.Snapshot = CaptureOfferingBowlSnapshot(offeringBowl)); } } } foreach (ItemStand itemStand in itemStands) { if (!((Object)(object)itemStand == (Object)null)) { ItemStandRuntimeState orAddItemStandRuntimeState = GetOrAddItemStandRuntimeState(itemStand); if (orAddItemStandRuntimeState.Snapshot == null) { ItemStandSnapshot itemStandSnapshot = (orAddItemStandRuntimeState.Snapshot = CaptureItemStandSnapshot(itemStand)); } } } } private static void RestoreRoot(Transform root) { RestoreComponents(((Component)root).GetComponentsInChildren(true), ((Component)root).GetComponentsInChildren(true)); } private static void RestoreComponents(IEnumerable offeringBowls, IEnumerable itemStands) { foreach (OfferingBowl offeringBowl in offeringBowls) { OfferingBowlRuntimeState offeringBowlRuntimeState = (((Object)(object)offeringBowl != (Object)null) ? ((Component)offeringBowl).GetComponent() : null); if (offeringBowlRuntimeState?.Snapshot != null && offeringBowlRuntimeState.Applied) { RestoreOfferingBowl(offeringBowl, offeringBowlRuntimeState.Snapshot); offeringBowlRuntimeState.Applied = false; offeringBowlRuntimeState.RespawnMinutes = 0f; } } foreach (ItemStand itemStand in itemStands) { ItemStandRuntimeState itemStandRuntimeState = (((Object)(object)itemStand != (Object)null) ? ((Component)itemStand).GetComponent() : null); if (itemStandRuntimeState?.Snapshot != null && itemStandRuntimeState.Applied) { RestoreItemStand(itemStand, itemStandRuntimeState.Snapshot); itemStandRuntimeState.Applied = false; } } } private static void ApplyOfferingBowl(OfferingBowl offeringBowl, AltarOfferingBowlDefinition entry, string prefabName) { string text = prefabName + "@offeringBowl"; if (entry.BossItem != null) { offeringBowl.m_bossItem = ResolveItemDrop(entry.BossItem, text + "/bossItem"); } if (entry.BossItems.HasValue) { offeringBowl.m_bossItems = Math.Max(1, entry.BossItems.Value); } if (entry.BossPrefab != null) { offeringBowl.m_bossPrefab = ResolveSpawnPrefab(entry.BossPrefab, text + "/bossPrefab"); } if (entry.ItemPrefab != null) { offeringBowl.m_itemPrefab = ResolveItemDrop(entry.ItemPrefab, text + "/itemPrefab"); } if (entry.SetGlobalKey != null) { offeringBowl.m_setGlobalKey = entry.SetGlobalKey; } if (entry.RenderSpawnAreaGizmos.HasValue) { offeringBowl.m_renderSpawnAreaGizmos = entry.RenderSpawnAreaGizmos.Value; } if (entry.AlertOnSpawn.HasValue) { offeringBowl.m_alertOnSpawn = entry.AlertOnSpawn.Value; } if (entry.SpawnBossDelay.HasValue) { offeringBowl.m_spawnBossDelay = Mathf.Max(0f, entry.SpawnBossDelay.Value); } float? num = entry.SpawnBossDistance?.Min; if (num.HasValue) { float valueOrDefault = num.GetValueOrDefault(); offeringBowl.m_spawnBossMinDistance = Mathf.Max(0f, valueOrDefault); } num = entry.SpawnBossDistance?.Max; if (num.HasValue) { float valueOrDefault2 = num.GetValueOrDefault(); offeringBowl.m_spawnBossMaxDistance = Mathf.Max(0f, valueOrDefault2); } if (entry.SpawnBossMaxYDistance.HasValue) { offeringBowl.m_spawnBossMaxYDistance = Mathf.Max(0f, entry.SpawnBossMaxYDistance.Value); } if (entry.GetSolidHeightMargin.HasValue) { offeringBowl.m_getSolidHeightMargin = Math.Max(0, entry.GetSolidHeightMargin.Value); } if (entry.EnableSolidHeightCheck.HasValue) { offeringBowl.m_enableSolidHeightCheck = entry.EnableSolidHeightCheck.Value; } if (entry.SpawnPointClearingRadius.HasValue) { offeringBowl.m_spawnPointClearingRadius = Mathf.Max(0f, entry.SpawnPointClearingRadius.Value); } if (entry.SpawnYOffset.HasValue) { offeringBowl.m_spawnYOffset = entry.SpawnYOffset.Value; } if (entry.UseItemStands.HasValue) { offeringBowl.m_useItemStands = entry.UseItemStands.Value; } if (entry.ItemStandPrefix != null) { offeringBowl.m_itemStandPrefix = entry.ItemStandPrefix; } if (entry.ItemStandMaxRange.HasValue) { offeringBowl.m_itemstandMaxRange = Mathf.Max(0f, entry.ItemStandMaxRange.Value); } OfferingBowlRuntimeState orAddOfferingBowlRuntimeState = GetOrAddOfferingBowlRuntimeState(offeringBowl); orAddOfferingBowlRuntimeState.Applied = true; orAddOfferingBowlRuntimeState.RespawnMinutes = (entry.RespawnMinutes.HasValue ? Mathf.Max(0f, entry.RespawnMinutes.Value) : 0f); BossRulesDebugLog.Client(string.Format("Altar offeringBowl applied context={0} bossPrefab={1} useItemStands={2} prefix='{3}' maxRange={4:0.##} respawnMinutes={5:0.##}.", text, ((Object)(object)offeringBowl.m_bossPrefab != (Object)null) ? GetPrefabName(offeringBowl.m_bossPrefab) : "", offeringBowl.m_useItemStands, offeringBowl.m_itemStandPrefix, offeringBowl.m_itemstandMaxRange, orAddOfferingBowlRuntimeState.RespawnMinutes)); } private static void ApplyConfiguredItemStands(IReadOnlyList definitions, IReadOnlyList relevantItemStands, Dictionary childItemStandsByPath, string prefabName, Transform root, OfferingBowl? offeringBowl) { HashSet exactMatchedItemStandIds = new HashSet(); List list = new List(); HashSet hashSet = new HashSet(StringComparer.Ordinal); foreach (AltarItemStandDefinition definition in definitions) { string text = (definition.Path ?? "").Trim(); if (text.Length == 0) { foreach (ItemStand relevantItemStand in relevantItemStands) { ApplyItemStand(relevantItemStand, definition, prefabName, root); } continue; } list.Add(definition); if (childItemStandsByPath.TryGetValue(text, out ItemStand value)) { exactMatchedItemStandIds.Add(((Object)value).GetInstanceID()); CaptureAuthoredItemStandSlot(prefabName, text, value, offeringBowl); BossRulesDebugLog.Client("Altar itemStand exact path match prefab=" + prefabName + " path='" + text + "' stand=" + ((Object)value).name + "."); ApplyItemStand(value, definition, prefabName, root); } else { hashSet.Add(text); } } if ((Object)(object)offeringBowl == (Object)null || list.Count == 0) { foreach (string item in hashSet) { WarnInvalidEntry("Entry '" + prefabName + "@itemStands[" + item + "]' references a missing ItemStand path."); } return; } List list2 = relevantItemStands.Where((ItemStand itemStand) => (Object)(object)itemStand != (Object)null && !exactMatchedItemStandIds.Contains(((Object)itemStand).GetInstanceID())).ToList(); if (list2.Count == 0) { foreach (string item2 in hashSet) { WarnInvalidEntry("Entry '" + prefabName + "@itemStands[" + item2 + "]' references a missing ItemStand path."); } return; } foreach (ItemStand item3 in list2) { LooseItemStandAuthoredPathsByInstance.Remove(item3); } TryStampLooseItemStandAuthoredPaths(offeringBowl, prefabName, list2); foreach (AltarItemStandDefinition item4 in list) { string path = (item4.Path ?? "").Trim(); string value2; ItemStand val = ((IEnumerable)list2).FirstOrDefault((Func)((ItemStand itemStand) => LooseItemStandAuthoredPathsByInstance.TryGetValue(itemStand, out value2) && string.Equals(value2, path, StringComparison.Ordinal))); if ((Object)(object)val == (Object)null) { if (hashSet.Contains(path)) { WarnInvalidEntry("Entry '" + prefabName + "@itemStands[" + path + "]' references a missing ItemStand path."); } } else { hashSet.Remove(path); BossRulesDebugLog.Client("Altar itemStand authored path remap prefab=" + prefabName + " path='" + path + "' stand=" + ((Object)val).name + "."); ApplyItemStand(val, item4, prefabName, root); } } } private static void ApplyItemStand(ItemStand itemStand, AltarItemStandDefinition entry, string prefabName, Transform root) { //IL_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: Unknown result type (might be due to invalid IL or missing references) string text = (string.IsNullOrWhiteSpace(entry.Path) ? (prefabName + "@itemStands") : (prefabName + "@itemStands[" + entry.Path + "]")); List list = null; ItemStandRuntimeState orAddItemStandRuntimeState = GetOrAddItemStandRuntimeState(itemStand); ItemStandRuntimeState itemStandRuntimeState = orAddItemStandRuntimeState; if (itemStandRuntimeState.Snapshot == null) { ItemStandSnapshot itemStandSnapshot = (itemStandRuntimeState.Snapshot = CaptureItemStandSnapshot(itemStand)); } if (entry.CanBeRemoved.HasValue) { itemStand.m_canBeRemoved = entry.CanBeRemoved.Value; } if (entry.AutoAttach.HasValue) { itemStand.m_autoAttach = entry.AutoAttach.Value; } if (entry.OrientationType != null && Enum.TryParse(entry.OrientationType, ignoreCase: true, out Orientation result)) { itemStand.m_orientationType = result; } if (entry.SupportedTypes != null) { itemStand.m_supportedTypes = ResolveItemStandTypes(entry.SupportedTypes, text + "/supportedTypes"); } if (entry.SupportedItems != null) { list = (itemStand.m_supportedItems = ResolveItemDropList(entry.SupportedItems, text + "/supportedItems")); BossRulesDebugLog.Client("Altar itemStand supportedItems resolved context=" + text + " requested=[" + FormatNames(entry.SupportedItems) + "] resolved=[" + FormatItemDrops(list) + "]."); } if (entry.UnsupportedItems != null) { itemStand.m_unsupportedItems = ResolveItemDropList(entry.UnsupportedItems, text + "/unsupportedItems"); BossRulesDebugLog.Client("Altar itemStand unsupportedItems resolved context=" + text + " requested=[" + FormatNames(entry.UnsupportedItems) + "] resolved=[" + FormatItemDrops(itemStand.m_unsupportedItems) + "]."); } else if (list != null) { RemoveSupportedItemsFromUnsupportedList(itemStand, list); } if (entry.PowerActivationDelay.HasValue) { itemStand.m_powerActivationDelay = Mathf.Max(0f, entry.PowerActivationDelay.Value); } if (entry.GuardianPower != null) { itemStand.m_guardianPower = ResolveStatusEffect(entry.GuardianPower, text + "/guardianPower"); } orAddItemStandRuntimeState.Applied = true; BossRulesDebugLog.Client($"Altar itemStand applied context={text} stand={((Object)itemStand).name} path='{GetRelativePath(root, ((Component)itemStand).transform)}' autoAttach={itemStand.m_autoAttach} supported=[{FormatItemDrops(itemStand.m_supportedItems)}] unsupported=[{FormatItemDrops(itemStand.m_unsupportedItems)}] applied={orAddItemStandRuntimeState.Applied}."); } private static List GetRelevantItemStands(OfferingBowl? offeringBowl, IEnumerable childItemStands) { List list = new List(); HashSet hashSet = new HashSet(); foreach (ItemStand childItemStand in childItemStands) { if ((Object)(object)childItemStand != (Object)null && hashSet.Add(((Object)childItemStand).GetInstanceID())) { list.Add(childItemStand); } } if ((Object)(object)offeringBowl == (Object)null || !offeringBowl.m_useItemStands) { return list; } foreach (ItemStand item in AltarItemStandHoverInfoFormatter.FindRelevantItemStands(offeringBowl)) { if ((Object)(object)item != (Object)null && hashSet.Add(((Object)item).GetInstanceID())) { list.Add(item); } } return list; } private static Dictionary BuildItemStandLookup(Transform root, IEnumerable itemStands) { Dictionary dictionary = new Dictionary(StringComparer.Ordinal); foreach (ItemStand itemStand in itemStands) { if ((Object)(object)itemStand != (Object)null) { dictionary[GetRelativePath(root, ((Component)itemStand).transform)] = itemStand; } } return dictionary; } private static void CaptureAuthoredItemStandSlot(string prefabName, string configuredPath, ItemStand itemStand, OfferingBowl? offeringBowl) { //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_008b: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)offeringBowl == (Object)null || (Object)(object)itemStand == (Object)null) { return; } string text = (prefabName ?? "").Trim(); string path = (configuredPath ?? "").Trim(); if (text.Length != 0 && path.Length != 0) { if (!AuthoredItemStandSlotsByPrefab.TryGetValue(text, out List value)) { value = new List(); AuthoredItemStandSlotsByPrefab[text] = value; } Vector3 offeringBowlLocalOffset = ((Component)offeringBowl).transform.InverseTransformPoint(((Component)itemStand).transform.position); int num = value.FindIndex((AuthoredItemStandSlotTemplate slot) => string.Equals(slot.Path, path, StringComparison.Ordinal)); AuthoredItemStandSlotTemplate authoredItemStandSlotTemplate = new AuthoredItemStandSlotTemplate { Path = path, OfferingBowlLocalOffset = offeringBowlLocalOffset }; if (num >= 0) { value[num] = authoredItemStandSlotTemplate; } else { value.Add(authoredItemStandSlotTemplate); } } } private static void TryStampLooseItemStandAuthoredPaths(OfferingBowl offeringBowl, string prefabName, IReadOnlyList relevantItemStands) { //IL_0165: Unknown result type (might be due to invalid IL or missing references) //IL_016a: 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_0193: 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_019c: Unknown result type (might be due to invalid IL or missing references) CleanupLooseItemStandAuthoredPaths(); string text = (prefabName ?? "").Trim(); if ((Object)(object)offeringBowl == (Object)null || text.Length == 0 || !AuthoredItemStandSlotsByPrefab.TryGetValue(text, out List value) || value.Count == 0 || relevantItemStands.Count == 0) { BossRulesDebugLog.Client($"Altar authored path remap skipped prefab={text} templates={(AuthoredItemStandSlotsByPrefab.TryGetValue(text, out List value2) ? value2.Count : 0)} relevant={relevantItemStands.Count}."); return; } HashSet hashSet = new HashSet(); HashSet hashSet2 = new HashSet(StringComparer.Ordinal); foreach (ItemStand relevantItemStand in relevantItemStands) { if (!((Object)(object)relevantItemStand == (Object)null) && LooseItemStandAuthoredPathsByInstance.TryGetValue(relevantItemStand, out string assignedPath) && !string.IsNullOrWhiteSpace(assignedPath) && value.Any((AuthoredItemStandSlotTemplate template) => string.Equals(template.Path, assignedPath, StringComparison.Ordinal))) { hashSet.Add(((Object)relevantItemStand).GetInstanceID()); hashSet2.Add(assignedPath); } } List<(float, ItemStand, AuthoredItemStandSlotTemplate)> list = new List<(float, ItemStand, AuthoredItemStandSlotTemplate)>(); foreach (ItemStand relevantItemStand2 in relevantItemStands) { if ((Object)(object)relevantItemStand2 == (Object)null || hashSet.Contains(((Object)relevantItemStand2).GetInstanceID())) { continue; } Vector3 val = ((Component)offeringBowl).transform.InverseTransformPoint(((Component)relevantItemStand2).transform.position); foreach (AuthoredItemStandSlotTemplate item4 in value) { if (!hashSet2.Contains(item4.Path)) { float item = Vector3.SqrMagnitude(val - item4.OfferingBowlLocalOffset); list.Add((item, relevantItemStand2, item4)); } } } BossRulesDebugLog.Client($"Altar authored path remap candidates prefab={text} templates={value.Count} relevant={relevantItemStands.Count} candidates={list.Count}."); list.Sort(((float Distance, ItemStand ItemStand, AuthoredItemStandSlotTemplate Template) left, (float Distance, ItemStand ItemStand, AuthoredItemStandSlotTemplate Template) right) => left.Distance.CompareTo(right.Distance)); foreach (var item5 in list) { ItemStand item2 = item5.Item2; AuthoredItemStandSlotTemplate item3 = item5.Item3; int instanceID = ((Object)item2).GetInstanceID(); if (!hashSet.Contains(instanceID) && !hashSet2.Contains(item3.Path)) { LooseItemStandAuthoredPathsByInstance[item2] = item3.Path; hashSet.Add(instanceID); hashSet2.Add(item3.Path); BossRulesDebugLog.Client("Altar authored path assigned prefab=" + text + " path='" + item3.Path + "' stand=" + ((Object)item2).name + "."); } } } private static void CleanupLooseItemStandAuthoredPaths() { List list = null; foreach (ItemStand key in LooseItemStandAuthoredPathsByInstance.Keys) { if (!((Object)(object)key != (Object)null) || !((Object)(object)((Component)key).gameObject != (Object)null)) { if (list == null) { list = new List(); } list.Add(key); } } if (list == null) { return; } foreach (ItemStand item in list) { LooseItemStandAuthoredPathsByInstance.Remove(item); } } internal static OfferingBowlSnapshot CaptureOfferingBowlSnapshot(OfferingBowl offeringBowl) { return new OfferingBowlSnapshot { BossItem = (NormalizeReferencePrefabName(((Object)(object)offeringBowl.m_bossItem != (Object)null) ? ((Component)offeringBowl.m_bossItem).gameObject : null) ?? ""), BossItems = offeringBowl.m_bossItems, BossPrefab = (NormalizeReferencePrefabName(offeringBowl.m_bossPrefab) ?? ""), ItemPrefab = (NormalizeReferencePrefabName(((Object)(object)offeringBowl.m_itemPrefab != (Object)null) ? ((Component)offeringBowl.m_itemPrefab).gameObject : null) ?? ""), SetGlobalKey = offeringBowl.m_setGlobalKey, RenderSpawnAreaGizmos = offeringBowl.m_renderSpawnAreaGizmos, AlertOnSpawn = offeringBowl.m_alertOnSpawn, SpawnBossDelay = offeringBowl.m_spawnBossDelay, SpawnBossMaxDistance = offeringBowl.m_spawnBossMaxDistance, SpawnBossMinDistance = offeringBowl.m_spawnBossMinDistance, SpawnBossMaxYDistance = offeringBowl.m_spawnBossMaxYDistance, GetSolidHeightMargin = offeringBowl.m_getSolidHeightMargin, EnableSolidHeightCheck = offeringBowl.m_enableSolidHeightCheck, SpawnPointClearingRadius = offeringBowl.m_spawnPointClearingRadius, SpawnYOffset = offeringBowl.m_spawnYOffset, UseItemStands = offeringBowl.m_useItemStands, ItemStandPrefix = offeringBowl.m_itemStandPrefix, ItemStandMaxRange = offeringBowl.m_itemstandMaxRange }; } internal unsafe static ItemStandSnapshot CaptureItemStandSnapshot(ItemStand itemStand) { return new ItemStandSnapshot { CanBeRemoved = itemStand.m_canBeRemoved, AutoAttach = itemStand.m_autoAttach, OrientationType = ((object)Unsafe.As(ref itemStand.m_orientationType)/*cast due to .constrained prefix*/).ToString(), SupportedTypes = itemStand.m_supportedTypes.Select((ItemType type) => ((object)(*(ItemType*)(&type))/*cast due to .constrained prefix*/).ToString()).ToList(), SupportedItems = (from item in itemStand.m_supportedItems where (Object)(object)item != (Object)null select NormalizeReferencePrefabName(((Component)item).gameObject) ?? "" into name where name.Length > 0 select name).ToList(), UnsupportedItems = (from item in itemStand.m_unsupportedItems where (Object)(object)item != (Object)null select NormalizeReferencePrefabName(((Component)item).gameObject) ?? "" into name where name.Length > 0 select name).ToList(), PowerActivationDelay = itemStand.m_powerActivationDelay, GuardianPower = (((Object)(object)itemStand.m_guardianPower != (Object)null) ? ((Object)itemStand.m_guardianPower).name : "") }; } private static void RestoreOfferingBowl(OfferingBowl offeringBowl, OfferingBowlSnapshot snapshot) { offeringBowl.m_bossItem = ResolveItemDrop(snapshot.BossItem, null); offeringBowl.m_bossItems = snapshot.BossItems; offeringBowl.m_bossPrefab = ResolveSpawnPrefab(snapshot.BossPrefab, null); offeringBowl.m_itemPrefab = ResolveItemDrop(snapshot.ItemPrefab, null); offeringBowl.m_setGlobalKey = snapshot.SetGlobalKey; offeringBowl.m_renderSpawnAreaGizmos = snapshot.RenderSpawnAreaGizmos; offeringBowl.m_alertOnSpawn = snapshot.AlertOnSpawn; offeringBowl.m_spawnBossDelay = snapshot.SpawnBossDelay; offeringBowl.m_spawnBossMaxDistance = snapshot.SpawnBossMaxDistance; offeringBowl.m_spawnBossMinDistance = snapshot.SpawnBossMinDistance; offeringBowl.m_spawnBossMaxYDistance = snapshot.SpawnBossMaxYDistance; offeringBowl.m_getSolidHeightMargin = snapshot.GetSolidHeightMargin; offeringBowl.m_enableSolidHeightCheck = snapshot.EnableSolidHeightCheck; offeringBowl.m_spawnPointClearingRadius = snapshot.SpawnPointClearingRadius; offeringBowl.m_spawnYOffset = snapshot.SpawnYOffset; offeringBowl.m_useItemStands = snapshot.UseItemStands; offeringBowl.m_itemStandPrefix = snapshot.ItemStandPrefix; offeringBowl.m_itemstandMaxRange = snapshot.ItemStandMaxRange; } private static void RestoreItemStand(ItemStand itemStand, ItemStandSnapshot snapshot) { //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) itemStand.m_canBeRemoved = snapshot.CanBeRemoved; itemStand.m_autoAttach = snapshot.AutoAttach; if (Enum.TryParse(snapshot.OrientationType, ignoreCase: true, out Orientation result)) { itemStand.m_orientationType = result; } itemStand.m_supportedTypes = (from type in snapshot.SupportedTypes.Select(ParseItemStandType) where type.HasValue select type.Value).ToList(); itemStand.m_supportedItems = ResolveItemDropList(snapshot.SupportedItems, null); itemStand.m_unsupportedItems = ResolveItemDropList(snapshot.UnsupportedItems, null); itemStand.m_powerActivationDelay = snapshot.PowerActivationDelay; itemStand.m_guardianPower = ResolveStatusEffect(snapshot.GuardianPower, null); } private static OfferingBowlRuntimeState GetOrAddOfferingBowlRuntimeState(OfferingBowl offeringBowl) { return ((Component)offeringBowl).GetComponent() ?? ((Component)offeringBowl).gameObject.AddComponent(); } private static ItemStandRuntimeState GetOrAddItemStandRuntimeState(ItemStand itemStand) { return ((Component)itemStand).GetComponent() ?? ((Component)itemStand).gameObject.AddComponent(); } private static long GetOfferingBowlLastUseTicks(OfferingBowl offeringBowl, OfferingBowlRuntimeState state) { if (state.LocalLastUseTicks > 0) { return state.LocalLastUseTicks; } ZNetView componentInParent = ((Component)offeringBowl).GetComponentInParent(); ZDO obj = ((componentInParent != null && componentInParent.IsValid()) ? componentInParent.GetZDO() : null); if (obj == null) { return 0L; } return obj.GetLong(OfferingBowlLastUseTicksKey, 0L); } private static string FormatNames(IEnumerable? names) { if (names != null) { return string.Join(",", names.Where((string name) => !string.IsNullOrWhiteSpace(name))); } return ""; } private static string FormatItemDrops(IEnumerable? itemDrops) { if (itemDrops == null) { return ""; } return string.Join(",", from itemDrop in itemDrops where (Object)(object)itemDrop != (Object)null select NormalizeReferencePrefabName(((Component)itemDrop).gameObject) ?? ((Object)itemDrop).name ?? "" into name where name.Length > 0 select name); } private static bool HasOverride(AltarConfigurationEntry entry) { if (entry.OfferingBowl == null) { List itemStands = entry.ItemStands; if (itemStands != null) { return itemStands.Count > 0; } return false; } return true; } private static List ResolveItemStandTypes(List typeNames, string warnContext) { //IL_005c: Unknown result type (might be due to invalid IL or missing references) List list = new List(); foreach (string typeName in typeNames) { ItemType? val = ParseItemStandType(typeName); if (!val.HasValue) { WarnInvalidEntry("Entry '" + warnContext + "' uses unknown ItemDrop.ItemData.ItemType '" + typeName + "'."); } else { list.Add(val.Value); } } return list; } private static ItemType? ParseItemStandType(string? typeName) { //IL_002e: Unknown result type (might be due to invalid IL or missing references) string text = (typeName ?? "").Trim(); if (text.Length <= 0 || !Enum.TryParse(text, ignoreCase: true, out ItemType result)) { return null; } return result; } private static ItemDrop? ResolveItemDrop(string? prefabName, string? warnContext) { GameObject val = ResolveItemPrefab(prefabName, warnContext); if (!((Object)(object)val != (Object)null)) { return null; } return val.GetComponent(); } private static List ResolveItemDropList(List prefabNames, string? warnContext) { List list = new List(); for (int i = 0; i < prefabNames.Count; i++) { ItemDrop val = ResolveItemDrop(prefabNames[i], (warnContext == null) ? null : $"{warnContext}[{i}]"); if ((Object)(object)val != (Object)null) { list.Add(val); } } return list; } private static GameObject? ResolveItemPrefab(string? prefabName, string? warnContext) { string text = (prefabName ?? "").Trim(); if (text.Length == 0) { return null; } ObjectDB instance = ObjectDB.instance; object obj = ((instance != null) ? instance.GetItemPrefab(text) : null); if (obj == null) { ZNetScene instance2 = ZNetScene.instance; obj = ((instance2 != null) ? instance2.GetPrefab(text) : null); } GameObject val = (GameObject)obj; if ((Object)(object)val == (Object)null) { WarnInvalidEntry((warnContext == null) ? null : ("Entry '" + warnContext + "' references unknown item prefab '" + text + "'.")); return null; } ItemDrop val2 = default(ItemDrop); if (!val.TryGetComponent(ref val2)) { WarnInvalidEntry((warnContext == null) ? null : ("Entry '" + warnContext + "' references '" + text + "', but it is not an item prefab.")); return null; } return val; } private static GameObject? ResolveSpawnPrefab(string? prefabName, string? warnContext) { string text = (prefabName ?? "").Trim(); if (text.Length == 0) { return null; } ZNetScene instance = ZNetScene.instance; object obj = ((instance != null) ? instance.GetPrefab(text) : null); if (obj == null) { ObjectDB instance2 = ObjectDB.instance; obj = ((instance2 != null) ? instance2.GetItemPrefab(text) : null); } GameObject val = (GameObject)obj; if ((Object)(object)val == (Object)null) { WarnInvalidEntry((warnContext == null) ? null : ("Entry '" + warnContext + "' references unknown spawn prefab '" + text + "'.")); } return val; } private static StatusEffect? ResolveStatusEffect(string? statusEffectName, string? warnContext) { string trimmed = (statusEffectName ?? "").Trim(); if (trimmed.Length == 0) { return null; } ObjectDB instance = ObjectDB.instance; StatusEffect val = ((instance != null) ? instance.GetStatusEffect(StringExtensionMethods.GetStableHashCode(trimmed)) : null); if ((Object)(object)val != (Object)null) { return val; } val = ((IEnumerable)ObjectDB.instance?.m_StatusEffects).FirstOrDefault((Func)((StatusEffect effect) => string.Equals(((Object)effect).name, trimmed, StringComparison.OrdinalIgnoreCase) || string.Equals(effect.m_name, trimmed, StringComparison.OrdinalIgnoreCase))); if ((Object)(object)val == (Object)null) { WarnInvalidEntry((warnContext == null) ? null : ("Entry '" + warnContext + "' references unknown status effect '" + trimmed + "'.")); } return val; } private static void RemoveSupportedItemsFromUnsupportedList(ItemStand itemStand, List supportedItems) { if (itemStand.m_unsupportedItems != null && itemStand.m_unsupportedItems.Count != 0 && supportedItems.Count != 0) { HashSet supportedNames = (from item in supportedItems select item?.m_itemData?.m_shared?.m_name ?? "" into name where name.Length > 0 select name).ToHashSet(StringComparer.OrdinalIgnoreCase); itemStand.m_unsupportedItems = itemStand.m_unsupportedItems.Where((ItemDrop item) => (Object)(object)item != (Object)null && !supportedNames.Contains(item.m_itemData.m_shared.m_name)).ToList(); } } internal static string GetRelativePath(Transform root, Transform target) { if ((Object)(object)target == (Object)(object)root) { return "."; } List list = new List(); Transform val = target; while ((Object)(object)val != (Object)null && (Object)(object)val != (Object)(object)root) { list.Add($"{((Object)val).name}[{GetSameNameSiblingIndex(val)}]"); val = val.parent; } list.Reverse(); return string.Join("/", list); } private static int GetSameNameSiblingIndex(Transform transform) { //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Expected O, but got Unknown if ((Object)(object)transform.parent == (Object)null) { return 0; } int num = 0; foreach (Transform item in transform.parent) { Transform val = item; if (val == transform) { return num; } if (string.Equals(((Object)val).name, ((Object)transform).name, StringComparison.Ordinal)) { num++; } } return num; } private static string? NormalizeReferencePrefabName(GameObject? prefab) { if (!((Object)(object)prefab != (Object)null)) { return null; } return TrimCloneSuffix(((Object)prefab).name); } private static string TrimCloneSuffix(string? name) { string text = (name ?? "").Trim(); if (!text.EndsWith("(Clone)", StringComparison.Ordinal)) { return text; } return text.Substring(0, text.Length - "(Clone)".Length).TrimEnd(Array.Empty()); } private static void WarnInvalidEntry(string? message) { if (!string.IsNullOrWhiteSpace(message)) { BossRulesPlugin.BossRulesLogger.LogWarning((object)message); } } private static void QueueOfferingBowlBossSpawnAttemptLocked(OfferingBowl offeringBowl, Vector3 spawnPoint, string refundPayload, float extraLifetimeSeconds, string verb) { //IL_0089: 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_009e: 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_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0118: Unknown result type (might be due to invalid IL or missing references) //IL_0122: Unknown result type (might be due to invalid IL or missing references) string prefabName = GetPrefabName(offeringBowl.m_bossPrefab); int stableHashCode = StringExtensionMethods.GetStableHashCode(prefabName); for (int num = PendingAltarBossSpawns.Count - 1; num >= 0; num--) { PendingAltarBossSpawn pendingAltarBossSpawn = PendingAltarBossSpawns[num]; if (pendingAltarBossSpawn.BossPrefabHash == stableHashCode && string.Equals(pendingAltarBossSpawn.RefundPayload, refundPayload, StringComparison.Ordinal) && Vector3.SqrMagnitude(pendingAltarBossSpawn.SpawnPoint - spawnPoint) <= 1f) { PendingAltarBossSpawns.RemoveAt(num); } } PendingAltarBossSpawn pendingAltarBossSpawn2 = new PendingAltarBossSpawn { BossPrefabName = prefabName, BossPrefabHash = stableHashCode, SpawnPoint = spawnPoint, RefundPoint = (((Object)(object)((Component)offeringBowl).transform != (Object)null) ? ((Component)offeringBowl).transform.position : spawnPoint), RefundPayload = refundPayload, ExpiresAt = Time.time + 5f + Math.Max(0f, extraLifetimeSeconds) }; PendingAltarBossSpawns.Add(pendingAltarBossSpawn2); _nextAltarSpawnMarkerRetryAt = 0f; BossRulesDebugLog.Client(string.Format("Altar refund capture {0} boss={1} useItemStands={2} payload='{3}' spawn={4} refundPoint={5} expiresIn={6}s.", verb, prefabName, offeringBowl.m_useItemStands, FormatRefundPayloadForLog(refundPayload), BossRulesDebugLog.FormatVector3(spawnPoint), BossRulesDebugLog.FormatVector3(pendingAltarBossSpawn2.RefundPoint), (5f + Math.Max(0f, extraLifetimeSeconds)).ToString("0.###", CultureInfo.InvariantCulture))); } internal static void ProcessPendingAltarSummonMarkers() { if ((Object)(object)ZNet.instance == (Object)null || PendingAltarBossSpawns.Count == 0 || Time.time < _nextAltarSpawnMarkerRetryAt) { return; } lock (Sync) { TryMarkNearbyPendingAltarSummonsLocked(); _nextAltarSpawnMarkerRetryAt = Time.time + 0.25f; } } private static void TryMarkNearbyPendingAltarSummonsLocked() { //IL_0078: Unknown result type (might be due to invalid IL or missing references) if (PendingAltarBossSpawns.Count == 0 || (Object)(object)ZNetScene.instance == (Object)null) { return; } float time = Time.time; PendingAltarBossSpawnRemovals.Clear(); foreach (PendingAltarBossSpawn pendingAltarBossSpawn in PendingAltarBossSpawns) { if (time >= pendingAltarBossSpawn.ExpiresAt) { PendingAltarBossSpawnRemovals.Add(pendingAltarBossSpawn); BossRulesDebugLog.Client("Altar refund marker expired boss=" + pendingAltarBossSpawn.BossPrefabName + " spawn=" + BossRulesDebugLog.FormatVector3(pendingAltarBossSpawn.SpawnPoint) + " payload='" + FormatRefundPayloadForLog(pendingAltarBossSpawn.RefundPayload) + "'."); } else if (TryMarkNearbyPendingAltarSummonLocked(pendingAltarBossSpawn)) { PendingAltarBossSpawnRemovals.Add(pendingAltarBossSpawn); } } foreach (PendingAltarBossSpawn pendingAltarBossSpawnRemoval in PendingAltarBossSpawnRemovals) { PendingAltarBossSpawns.Remove(pendingAltarBossSpawnRemoval); } PendingAltarBossSpawnRemovals.Clear(); } private static bool TryMarkNearbyPendingAltarSummonLocked(PendingAltarBossSpawn pending) { //IL_00f6: Unknown result type (might be due to invalid IL or missing references) //IL_0061: 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_006c: Unknown result type (might be due to invalid IL or missing references) Character val = default(Character); foreach (KeyValuePair instance in ZNetScene.instance.m_instances) { ZDO key = instance.Key; ZNetView value = instance.Value; if (key != null && !((Object)(object)value == (Object)null) && !((Object)(object)((Component)value).gameObject == (Object)null)) { int prefab = key.GetPrefab(); if ((prefab == 0 || prefab == pending.BossPrefabHash) && !(Vector3.SqrMagnitude(key.GetPosition() - pending.SpawnPoint) > 16384f) && ((Component)value).TryGetComponent(ref val) && (prefab != 0 || string.Equals(GetPrefabName(((Component)val).gameObject), pending.BossPrefabName, StringComparison.OrdinalIgnoreCase)) && TryMarkAltarSummonedCharacterLocked(val, key, pending)) { return true; } } } BossRulesDebugLog.Client($"Altar refund marker missed boss={pending.BossPrefabName} hash={pending.BossPrefabHash} spawn={BossRulesDebugLog.FormatVector3(pending.SpawnPoint)} payload='{FormatRefundPayloadForLog(pending.RefundPayload)}'."); return false; } internal static void TryMarkCreatedAltarSummonZdo(int prefabHashHint, ZDO? zdo) { if ((Object)(object)ZNet.instance == (Object)null || zdo == null || ((ZDOID)(ref zdo.m_uid)).IsNone() || zdo.GetBool(AltarSummonKey, false) || PendingAltarBossSpawns.Count == 0) { return; } lock (Sync) { for (int num = PendingAltarBossSpawns.Count - 1; num >= 0; num--) { PendingAltarBossSpawn pending = PendingAltarBossSpawns[num]; if (TryMarkCreatedAltarSummonZdoLocked(prefabHashHint, zdo, pending)) { PendingAltarBossSpawns.RemoveAt(num); break; } } } } private static bool TryMarkCreatedAltarSummonZdoLocked(int prefabHashHint, ZDO zdo, PendingAltarBossSpawn pending) { //IL_002b: 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) //IL_006d: 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_00af: Unknown result type (might be due to invalid IL or missing references) if (zdo.GetBool(AltarSummonKey, false)) { return false; } int num = zdo.GetPrefab(); if (num == 0) { num = prefabHashHint; } if (num == 0 || num != pending.BossPrefabHash) { return false; } if (Vector3.SqrMagnitude(zdo.GetPosition() - pending.SpawnPoint) > 16384f) { return false; } zdo.Set(AltarSummonKey, true); zdo.Set(AltarRefundsKey, pending.RefundPayload); zdo.Set(AltarRefundPointKey, SerializeVector3(pending.RefundPoint)); BossRulesDebugLog.Client($"Altar refund marker applied on created ZDO boss={pending.BossPrefabName} zdo={zdo.m_uid} payload='{FormatRefundPayloadForLog(pending.RefundPayload)}' refundPoint={BossRulesDebugLog.FormatVector3(pending.RefundPoint)}."); return true; } private static bool TryMarkAltarSummonedCharacterLocked(Character character, ZDO? zdo) { if (zdo == null || zdo.GetBool(AltarSummonKey, false)) { return false; } for (int num = PendingAltarBossSpawns.Count - 1; num >= 0; num--) { PendingAltarBossSpawn pending = PendingAltarBossSpawns[num]; if (TryMarkAltarSummonedCharacterLocked(character, zdo, pending)) { PendingAltarBossSpawns.RemoveAt(num); return true; } } return false; } private static bool TryMarkAltarSummonedCharacterLocked(Character character, ZDO zdo, PendingAltarBossSpawn pending) { //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_0063: 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_006a: Unknown result type (might be due to invalid IL or missing references) //IL_00e4: Unknown result type (might be due to invalid IL or missing references) //IL_0110: Unknown result type (might be due to invalid IL or missing references) //IL_012c: Unknown result type (might be due to invalid IL or missing references) //IL_0092: 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_00a9: 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) if (zdo.GetBool(AltarSummonKey, false)) { return false; } if (zdo.GetPrefab() != pending.BossPrefabHash) { string prefabName = GetPrefabName(((Component)character).gameObject); if (!string.Equals(prefabName, pending.BossPrefabName, StringComparison.OrdinalIgnoreCase)) { BossRulesDebugLog.Client($"Altar refund marker skipped prefab mismatch expected={pending.BossPrefabName} actual={prefabName} zdo={zdo.m_uid}."); return false; } } Vector3 centerPoint = character.GetCenterPoint(); if (Vector3.SqrMagnitude(centerPoint - pending.SpawnPoint) > 16384f) { BossRulesDebugLog.Client($"Altar refund marker skipped distance boss={pending.BossPrefabName} zdo={zdo.m_uid} position={BossRulesDebugLog.FormatVector3(centerPoint)} spawn={BossRulesDebugLog.FormatVector3(pending.SpawnPoint)}."); return false; } zdo.Set(AltarSummonKey, true); zdo.Set(AltarRefundsKey, pending.RefundPayload); zdo.Set(AltarRefundPointKey, SerializeVector3(pending.RefundPoint)); DespawnRulesManager.TryTrackLoadedDespawnTarget(character); BossRulesDebugLog.Client($"Altar refund marker applied boss={pending.BossPrefabName} zdo={zdo.m_uid} payload='{FormatRefundPayloadForLog(pending.RefundPayload)}' refundPoint={BossRulesDebugLog.FormatVector3(pending.RefundPoint)}."); return true; } internal static bool TryResolveAltarSummonRefunds(ZDO? zdo, out IReadOnlyCollection refunds) { //IL_0039: 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_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0163: 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) refunds = (IReadOnlyCollection)(object)Array.Empty(); if (zdo == null || !zdo.GetBool(AltarSummonKey, false)) { return false; } string text = zdo.GetString(AltarRefundsKey, ""); if (string.IsNullOrWhiteSpace(text)) { BossRulesDebugLog.Client($"Altar refund resolve skipped for zdo={zdo.m_uid}: altar summon marker exists but payload is empty."); return false; } Vector3? dropPointOverride = null; string text2 = zdo.GetString(AltarRefundPointKey, ""); if (!string.IsNullOrWhiteSpace(text2)) { if (TryDeserializeVector3(text2, out var value)) { dropPointOverride = value; } else { BossRulesDebugLog.Client($"Altar refund point ignored for zdo={zdo.m_uid}: invalid payload='{text2}'."); } } List list = new List(); string[] array = text.Split(new char[1] { ';' }, StringSplitOptions.RemoveEmptyEntries); for (int i = 0; i < array.Length; i++) { string[] array2 = array[i].Split(new char[1] { ':' }, 2); string text3 = ((array2.Length != 0) ? array2[0].Trim() : ""); if (text3.Length != 0) { int amount = 1; if (array2.Length > 1 && int.TryParse(array2[1], NumberStyles.Integer, CultureInfo.InvariantCulture, out var result)) { amount = Math.Max(1, result); } if (BossRulesRuntime.TryResolveItemPrefab(text3, "altar auto refund", out GameObject prefab)) { list.Add(new DespawnRefundDrop(prefab, amount, dropPointOverride)); } } } refunds = list; BossRulesDebugLog.Client(string.Format("Altar refund resolved for zdo={0}: payload='{1}' refundPoint={2} resolved={3}.", zdo.m_uid, FormatRefundPayloadForLog(text), dropPointOverride.HasValue ? BossRulesDebugLog.FormatVector3(dropPointOverride.Value) : "", BossRulesDebugLog.FormatRefunds(list))); return list.Count > 0; } private static string BuildOfferingRefundPayload(OfferingBowl offeringBowl) { Dictionary refunds = new Dictionary(StringComparer.OrdinalIgnoreCase); int num = 0; int num2 = 0; string text = ""; int num3 = 0; if (offeringBowl.m_useItemStands) { foreach (ItemStand item in AltarItemStandHoverInfoFormatter.FindRelevantItemStands(offeringBowl)) { num++; if ((Object)(object)item == (Object)null || !TryGetAttachedItemName(item, out string itemName)) { if (BossRulesDebugLog.IsClientEnabled) { BossRulesDebugLog.Client("Altar refund item stand scan found no attachment stand='" + (((Object)(object)item != (Object)null) ? ((Object)item).name : "") + "' altar='" + ((Object)offeringBowl).name + "'."); } } else if (itemName.Length > 0) { num2++; AddRefund(refunds, itemName, 1); } } } else if ((Object)(object)offeringBowl.m_bossItem != (Object)null) { string text2 = NormalizeReferencePrefabName(((Component)offeringBowl.m_bossItem).gameObject) ?? ""; if (text2.Length > 0) { text = text2; num3 = Math.Max(1, offeringBowl.m_bossItems); AddRefund(refunds, text, num3); } } string text3 = SerializeRefundPayload(refunds); if (BossRulesDebugLog.IsClientEnabled) { if (offeringBowl.m_useItemStands) { BossRulesDebugLog.Client($"Altar refund item stand scan altar='{((Object)offeringBowl).name}' scanned={num} attached={num2} payload='{FormatRefundPayloadForLog(text3)}'."); } else if ((Object)(object)offeringBowl.m_bossItem != (Object)null) { BossRulesDebugLog.Client($"Altar refund direct offering altar='{((Object)offeringBowl).name}' item={text} amount={num3} payload='{FormatRefundPayloadForLog(text3)}'."); } } return text3; } private static string ConsumePreparedOfferingRefundPayload(OfferingBowl offeringBowl) { OfferingBowlRuntimeState component = ((Component)offeringBowl).GetComponent(); string text = component?.PendingRefundPayload; if (text != null) { component.PendingRefundPayload = null; BossRulesDebugLog.Client("Altar refund using prepared payload altar='" + ((Object)offeringBowl).name + "' payload='" + FormatRefundPayloadForLog(text) + "'."); return text; } string text2 = BuildOfferingRefundPayload(offeringBowl); BossRulesDebugLog.Client("Altar refund using delayed fallback payload altar='" + ((Object)offeringBowl).name + "' payload='" + FormatRefundPayloadForLog(text2) + "'."); return text2; } private static string SerializeRefundPayload(Dictionary refunds) { return string.Join(";", from pair in refunds.OrderBy, string>((KeyValuePair pair) => pair.Key, StringComparer.OrdinalIgnoreCase) select pair.Key + ":" + pair.Value.ToString(CultureInfo.InvariantCulture)); } private static string SerializeVector3(Vector3 value) { return string.Join(",", value.x.ToString("R", CultureInfo.InvariantCulture), value.y.ToString("R", CultureInfo.InvariantCulture), value.z.ToString("R", CultureInfo.InvariantCulture)); } private static bool TryDeserializeVector3(string raw, out Vector3 value) { //IL_0001: 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_0086: Unknown result type (might be due to invalid IL or missing references) value = default(Vector3); string[] array = (raw ?? "").Split(new char[1] { ',' }); if (array.Length != 3) { return false; } if (!float.TryParse(array[0].Trim(), NumberStyles.Float | NumberStyles.AllowThousands, CultureInfo.InvariantCulture, out var result) || !float.TryParse(array[1].Trim(), NumberStyles.Float | NumberStyles.AllowThousands, CultureInfo.InvariantCulture, out var result2) || !float.TryParse(array[2].Trim(), NumberStyles.Float | NumberStyles.AllowThousands, CultureInfo.InvariantCulture, out var result3)) { return false; } value = new Vector3(result, result2, result3); return true; } private static string FormatRefundPayloadForLog(string? payload) { if (!string.IsNullOrWhiteSpace(payload)) { return payload; } return ""; } private static bool TryGetAttachedItemName(ItemStand itemStand, out string itemName) { itemName = ""; ZNetView val = (((Object)(object)itemStand != (Object)null) ? ((Component)itemStand).GetComponent() : null); ZDO val2 = (((Object)(object)val != (Object)null && val.IsValid()) ? val.GetZDO() : null); if (val2 == null) { return false; } itemName = (val2.GetString(ZDOVars.s_item, "") ?? "").Trim(); return itemName.Length > 0; } private static void AddRefund(Dictionary refunds, string itemName, int amount) { if (!string.IsNullOrWhiteSpace(itemName) && amount > 0) { refunds[itemName] = (refunds.TryGetValue(itemName, out var value) ? (value + amount) : amount); } } } [HarmonyPatch(typeof(Location), "Awake")] internal static class LocationAwakeAltarPatch { private static void Postfix(Location __instance) { AltarRuntime.RegisterLocation(__instance); } } [HarmonyPatch(typeof(Location), "OnDestroy")] internal static class LocationOnDestroyAltarPatch { private static void Prefix(Location __instance) { AltarRuntime.UnregisterLocation(__instance); } } [HarmonyPatch(typeof(LocationProxy), "SpawnLocation")] internal static class LocationProxySpawnLocationAltarPatch { private static readonly FieldRef InstanceRef = AccessTools.FieldRefAccess("m_instance"); private static void Postfix(LocationProxy __instance, bool __result) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) if (__result && !((Object)(object)__instance == (Object)null) && (AltarLocationResolver.TryResolveZoneLocationPrefabName(((Component)__instance).transform.position, out string prefabName) || AltarLocationResolver.TryResolveLocationProxyPrefabName(__instance, out prefabName))) { AltarLocationResolver.RecordLocationProxyResolvedPrefab(__instance, prefabName); AltarRuntime.ReconcileSpawnedLocationRoot(InstanceRef.Invoke(__instance), prefabName); } } } [HarmonyPatch(typeof(OfferingBowl), "Awake")] internal static class OfferingBowlAwakeAltarPatch { private static void Postfix(OfferingBowl __instance) { OfferingBowlHoverInfoFormatter.RegisterOfferingBowl(__instance); AltarRuntime.ReconcileLooseOfferingBowl(__instance); } } [HarmonyPatch(typeof(OfferingBowl), "GetHoverText")] internal static class OfferingBowlGetHoverTextAltarPatch { private static void Postfix(OfferingBowl __instance, ref string __result) { if (BossRulesConfig.ShouldShowOfferingBowlHoverInfo()) { AltarRuntime.ReconcileLooseOfferingBowl(__instance); __result = OfferingBowlHoverInfoFormatter.AppendInfo(__result, __instance); } } } [HarmonyPatch(typeof(OfferingBowl), "Interact")] internal static class OfferingBowlInteractAltarPatch { private static bool Prefix(OfferingBowl __instance, Humanoid user, bool hold, ref bool __result) { if (!BossRulesConfig.IsAltarRulesEnabled() || hold || !__instance.m_useItemStands) { return true; } AltarRuntime.ReconcileLooseOfferingBowl(__instance); AltarRuntime.OfferingBowlBlockResult result = AltarRuntime.EvaluateOfferingBowlBlock(__instance); if (!result.Blocked) { return true; } __result = true; AltarRuntime.NotifyOfferingBowlBlocked(__instance, user, result); return false; } } [HarmonyPatch(typeof(OfferingBowl), "UseItem")] internal static class OfferingBowlUseItemAltarPatch { private static bool Prefix(OfferingBowl __instance, Humanoid user, ref bool __result) { if (!BossRulesConfig.IsAltarRulesEnabled() || __instance.m_useItemStands) { return true; } AltarRuntime.ReconcileLooseOfferingBowl(__instance); AltarRuntime.OfferingBowlBlockResult result = AltarRuntime.EvaluateOfferingBowlBlock(__instance); if (!result.Blocked) { return true; } __result = true; AltarRuntime.NotifyOfferingBowlBlocked(__instance, user, result); return false; } } [HarmonyPatch(typeof(OfferingBowl), "RPC_SpawnBoss")] internal static class OfferingBowlRpcSpawnBossAltarPatch { private static bool Prefix(OfferingBowl __instance) { if ((Object)(object)ZNet.instance == (Object)null) { return true; } if (ZNet.instance.IsServer() && BossRulesConfig.IsAltarRulesEnabled() && AltarRuntime.EvaluateOfferingBowlBlock(__instance).Blocked) { return false; } if (BossRulesConfig.ShouldCaptureAltarSpawnRefunds()) { AltarRuntime.PrepareOfferingBowlRefundPayload(__instance); } return true; } } [HarmonyPatch(typeof(OfferingBowl), "DelayedSpawnBoss")] internal static class OfferingBowlDelayedSpawnBossAltarPatch { private static readonly FieldRef BossSpawnPointRef = AccessTools.FieldRefAccess("m_bossSpawnPoint"); private static void Prefix(OfferingBowl __instance) { //IL_0020: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)ZNet.instance != (Object)null && BossRulesConfig.ShouldCaptureAltarSpawnRefunds()) { AltarRuntime.BeginOfferingBowlBossSpawnAttempt(__instance, BossSpawnPointRef.Invoke(__instance)); } } private static void Postfix(OfferingBowl __instance) { //IL_0020: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)ZNet.instance != (Object)null && BossRulesConfig.ShouldCaptureAltarSpawnRefunds()) { AltarRuntime.FinalizeOfferingBowlBossSpawnAttempt(__instance, BossSpawnPointRef.Invoke(__instance)); } } } [HarmonyPatch(typeof(OfferingBowl), "SpawnBoss")] internal static class OfferingBowlSpawnBossAltarPatch { private static void Prefix(OfferingBowl __instance, Vector3 spawnPoint) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)ZNet.instance != (Object)null && BossRulesConfig.ShouldCaptureAltarSpawnRefunds()) { AltarRuntime.PrepareAndQueueOfferingBowlRefundPayload(__instance, spawnPoint); } } private static void Postfix(OfferingBowl __instance) { if (BossRulesConfig.IsAltarRulesEnabled()) { AltarRuntime.MarkOfferingBowlUsed(__instance); } } } [HarmonyPatch(typeof(OfferingBowl), "SpawnItem")] internal static class OfferingBowlSpawnItemAltarPatch { private static void Postfix(OfferingBowl __instance, bool __result) { if (BossRulesConfig.IsAltarRulesEnabled() && __result) { AltarRuntime.MarkOfferingBowlUsed(__instance); } } } [HarmonyPatch(typeof(ItemStand), "Awake")] internal static class ItemStandAwakeAltarPatch { private static void Postfix(ItemStand __instance) { AltarItemStandHoverInfoFormatter.RegisterItemStand(__instance); AltarRuntime.ReconcileLooseItemStand(__instance); } } [HarmonyPatch(typeof(ItemStand), "GetHoverText")] internal static class ItemStandGetHoverTextAltarPatch { private static void Postfix(ItemStand __instance, ref string __result) { if (BossRulesConfig.ShouldShowOfferingBowlHoverInfo()) { __result = AltarItemStandHoverInfoFormatter.AppendInfo(__result, __instance); } } } [HarmonyPatch(typeof(ItemStand), "Interact")] internal static class ItemStandInteractAltarPatch { private static void Prefix(ItemStand __instance) { AltarRuntime.ReconcileLooseItemStand(__instance); } } [HarmonyPatch(typeof(ItemStand), "UseItem")] internal static class ItemStandUseItemAltarPatch { private static void Prefix(ItemStand __instance) { AltarRuntime.ReconcileLooseItemStand(__instance); } } [HarmonyPatch(typeof(Character), "Awake")] internal static class CharacterAwakeBossRulesPatch { private static void Postfix(Character __instance) { AltarRuntime.TryMarkAltarSummonedCharacter(__instance); BossRulesManager.TrackBossCharacter(__instance); DespawnRulesManager.TryTrackLoadedDespawnTarget(__instance); } } [HarmonyPatch(typeof(Character), "OnDestroy")] internal static class CharacterOnDestroyBossRulesPatch { private static void Prefix(Character __instance) { BossRulesManager.UntrackBossCharacter(__instance); } } [HarmonyPatch(typeof(CreatureSpawner), "UpdateSpawner")] internal static class CreatureSpawnerUpdateSpawnerDuplicateBlockPatch { private static bool Prefix(CreatureSpawner __instance) { return !CreatureSpawnerDuplicateBlockRuntime.ShouldBlockUpdate(__instance); } } [HarmonyPatch(typeof(CreatureSpawner), "OnDestroy")] internal static class CreatureSpawnerOnDestroyDuplicateBlockPatch { private static void Prefix(CreatureSpawner __instance) { CreatureSpawnerDuplicateBlockRuntime.RemoveSpawner(__instance); } } [HarmonyPatch(typeof(Character), "RPC_Damage")] internal static class CharacterRpcDamageBossTamedPressurePatch { private static void Prefix(Character __instance, HitData hit) { BossTamedPressureRuntime.ApplyDamageMultipliers(__instance, hit); } } [HarmonyPatch] internal static class ZDOManCreateNewZdoDespawnPatch { private static MethodBase? TargetMethod() { return AccessTools.Method(typeof(ZDOMan), "CreateNewZDO", new Type[3] { typeof(ZDOID), typeof(Vector3), typeof(int) }, (Type[])null); } private static void Postfix(int prefabHashIn, ZDO __result) { AltarRuntime.TryMarkCreatedAltarSummonZdo(prefabHashIn, __result); DespawnRulesManager.QueueCreatedDespawnTarget(prefabHashIn, __result); } } [HarmonyPatch(typeof(ZNetView), "ResetZDO")] internal static class ZNetViewResetZdoDespawnPatch { private static void Prefix(ZNetView __instance) { DespawnRulesManager.TryPersistDespawnCountdownBeforeResetZdo(__instance); } } internal static class AltarLocationResolver { private static readonly Dictionary LocationPrefabNamesByHash = new Dictionary(); private static readonly Dictionary RuntimeLocationProxyPrefabsByInstance = new Dictionary(); private static readonly Dictionary RuntimeLocationProxyPrefabsByZdoId = new Dictionary(); private static readonly int LocationProxyResolvedPrefabZdoKey = StringExtensionMethods.GetStableHashCode("BossRules.location_proxy_prefab"); internal static void RecordLocationProxyResolvedPrefab(LocationProxy? proxy, string prefabName) { //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) string text = (prefabName ?? "").Trim(); if ((Object)(object)proxy == (Object)null || text.Length == 0) { return; } RuntimeLocationProxyPrefabsByInstance[proxy] = text; ZNetView component = ((Component)proxy).GetComponent(); ZDO val = ((component != null) ? component.GetZDO() : null); if (val != null) { if (val.m_uid != ZDOID.None) { RuntimeLocationProxyPrefabsByZdoId[val.m_uid] = text; } if (component.IsOwner()) { val.Set(LocationProxyResolvedPrefabZdoKey, text); } } } internal static bool TryResolveLocationPrefabName(Location? location, out string prefabName) { prefabName = ""; if ((Object)(object)location == (Object)null) { return false; } LocationProxy componentInParent = ((Component)location).GetComponentInParent(true); if ((Object)(object)componentInParent != (Object)null && TryResolveLocationProxyPrefabName(componentInParent, out prefabName)) { return true; } return TryGetLocationPrefabNameWithoutProxy(location, out prefabName); } internal static bool TryResolveLocationProxyPrefabName(LocationProxy? proxy, out string prefabName) { prefabName = ""; if ((Object)(object)proxy == (Object)null) { return false; } if (TryGetRecordedLocationProxyPrefabName(proxy, out prefabName)) { return true; } if (TryGetLocationProxyHashPrefabName(proxy, out prefabName)) { return true; } Location componentInChildren = ((Component)proxy).GetComponentInChildren(true); if ((Object)(object)componentInChildren != (Object)null) { return TryGetLocationPrefabNameWithoutProxy(componentInChildren, out prefabName); } return false; } internal static bool TryResolveZoneLocationPrefabName(Vector3 position, out string prefabName) { //IL_0016: 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_001c: 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_0034: Unknown result type (might be due to invalid IL or missing references) prefabName = ""; if ((Object)(object)ZoneSystem.instance == (Object)null) { return false; } Vector2i zone = ZoneSystem.GetZone(position); if (!ZoneSystem.instance.m_locationInstances.TryGetValue(zone, out var value)) { return false; } prefabName = GetZoneLocationPrefabName(value.m_location); return prefabName.Length > 0; } private static bool TryGetLocationPrefabNameWithoutProxy(Location location, out string prefabName) { //IL_0043: 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_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) prefabName = ""; if ((Object)(object)location == (Object)null) { return false; } string text = TrimCloneSuffix(((Object)((Component)location).gameObject).name); string text2 = TryGetLocationRootPrefabName(location); string text3 = ""; if ((Object)(object)ZoneSystem.instance != (Object)null) { Vector2i zone = ZoneSystem.GetZone(((Component)location).transform.position); if (ZoneSystem.instance.m_locationInstances.TryGetValue(zone, out var value)) { text3 = GetZoneLocationPrefabName(value.m_location); } } if (ShouldPreferLiveLocationPrefabName(text2, text3)) { prefabName = text2; return true; } if (ShouldPreferLiveLocationPrefabName(text, text3)) { prefabName = text; return true; } prefabName = ((text3.Length > 0) ? text3 : text); return prefabName.Length > 0; } private static bool TryGetRecordedLocationProxyPrefabName(LocationProxy proxy, out string prefabName) { //IL_0057: 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_006e: Unknown result type (might be due to invalid IL or missing references) prefabName = ""; ZNetView component = ((Component)proxy).GetComponent(); ZDO val = ((component != null) ? component.GetZDO() : null); if (val != null) { string text = (val.GetString(LocationProxyResolvedPrefabZdoKey, "") ?? "").Trim(); if (text.Length > 0) { RuntimeLocationProxyPrefabsByInstance[proxy] = text; prefabName = text; return true; } if (val.m_uid != ZDOID.None && RuntimeLocationProxyPrefabsByZdoId.TryGetValue(val.m_uid, out string value) && !string.IsNullOrWhiteSpace(value)) { RuntimeLocationProxyPrefabsByInstance[proxy] = value; prefabName = value; return true; } } if (RuntimeLocationProxyPrefabsByInstance.TryGetValue(proxy, out string value2)) { prefabName = value2 ?? ""; return prefabName.Length > 0; } return false; } private static bool TryGetLocationProxyHashPrefabName(LocationProxy proxy, out string prefabName) { prefabName = ""; ZNetView component = ((Component)proxy).GetComponent(); ZDO obj = ((component != null) ? component.GetZDO() : null); int num = ((obj != null) ? obj.GetInt(ZDOVars.s_location, 0) : 0); if (num == 0) { return false; } if (LocationPrefabNamesByHash.TryGetValue(num, out string value)) { prefabName = value ?? ""; return prefabName.Length > 0; } if ((Object)(object)ZoneSystem.instance == (Object)null) { return false; } foreach (ZoneLocation location in ZoneSystem.instance.m_locations) { string zoneLocationPrefabName = GetZoneLocationPrefabName(location); if (zoneLocationPrefabName.Length != 0 && StringExtensionMethods.GetStableHashCode(zoneLocationPrefabName) == num) { LocationPrefabNamesByHash[num] = zoneLocationPrefabName; prefabName = zoneLocationPrefabName; return true; } } LocationPrefabNamesByHash[num] = ""; return false; } private static string TryGetLocationRootPrefabName(Location location) { Transform val = null; LocationProxy componentInParent = ((Component)location).GetComponentInParent(true); if ((Object)(object)componentInParent != (Object)null) { Transform val2 = ((Component)location).transform; while ((Object)(object)val2 != (Object)null && (Object)(object)val2.parent != (Object)null) { if (val2.parent == ((Component)componentInParent).transform) { val = val2; break; } val2 = val2.parent; } } if (val == null) { val = GetRootTransform(((Component)location).transform); } if (!((Object)(object)val != (Object)null)) { return ""; } return TrimCloneSuffix(((Object)((Component)val).gameObject).name); } private static bool ShouldPreferLiveLocationPrefabName(string livePrefabName, string zonePrefabName) { if (livePrefabName.Length == 0 || string.Equals(livePrefabName, zonePrefabName, StringComparison.OrdinalIgnoreCase)) { return false; } if (zonePrefabName.Length > 0 && !livePrefabName.Contains(":") && (zonePrefabName.Contains(":") || string.Equals(GetLocationPrefabBaseName(zonePrefabName), livePrefabName, StringComparison.OrdinalIgnoreCase))) { return false; } if (!livePrefabName.Contains(":")) { return AltarRuntime.HasConfiguredPrefab(livePrefabName); } return true; } internal static string GetZoneLocationPrefabName(ZoneLocation? location) { return (location?.m_prefabName ?? location?.m_prefab.Name ?? "").Trim(); } private static string GetLocationPrefabBaseName(string prefabName) { string text = (prefabName ?? "").Trim(); int num = text.LastIndexOf(':'); if (num < 0) { return text; } return text.Substring(num + 1); } private static Transform GetRootTransform(Transform transform) { Transform val = transform; while ((Object)(object)val.parent != (Object)null) { val = val.parent; } return val; } private static string TrimCloneSuffix(string? name) { string text = (name ?? "").Trim(); if (!text.EndsWith("(Clone)", StringComparison.Ordinal)) { return text; } return text.Substring(0, text.Length - "(Clone)".Length).TrimEnd(Array.Empty()); } } internal static class OfferingBowlHoverInfoFormatter { private sealed class HoverInfoCacheEntry { public string Info { get; set; } = ""; public float ExpiresAt { get; set; } } private const float HoverInfoCacheSeconds = 0.25f; private static readonly List RegisteredOfferingBowls = new List(); private static readonly HashSet RegisteredOfferingBowlIds = new HashSet(); private static readonly Dictionary HoverInfoCache = new Dictionary(); internal static string AppendInfo(string baseText, OfferingBowl? offeringBowl) { if (!BossRulesConfig.ShouldShowOfferingBowlHoverInfo() || (Object)(object)offeringBowl == (Object)null) { return baseText ?? ""; } string cachedInfo = GetCachedInfo(offeringBowl); if (cachedInfo.Length == 0) { return baseText ?? ""; } if (!string.IsNullOrWhiteSpace(baseText)) { return baseText + "\n" + cachedInfo; } return cachedInfo; } internal static void RegisterOfferingBowl(OfferingBowl? offeringBowl) { if (!((Object)(object)offeringBowl == (Object)null) && RegisteredOfferingBowlIds.Add(((Object)offeringBowl).GetInstanceID())) { RegisteredOfferingBowls.Add(offeringBowl); } } internal static IReadOnlyList GetKnownOfferingBowls() { CleanupRegisteredOfferingBowls(); if (RegisteredOfferingBowls.Count == 0) { OfferingBowl[] array = Object.FindObjectsByType((FindObjectsSortMode)0); for (int i = 0; i < array.Length; i++) { RegisterOfferingBowl(array[i]); } } return RegisteredOfferingBowls; } internal static void ClearRuntimeCaches() { HoverInfoCache.Clear(); } private static string GetCachedInfo(OfferingBowl offeringBowl) { int instanceID = ((Object)offeringBowl).GetInstanceID(); float realtimeSinceStartup = Time.realtimeSinceStartup; if (HoverInfoCache.TryGetValue(instanceID, out HoverInfoCacheEntry value) && value.ExpiresAt > realtimeSinceStartup) { return value.Info; } string text = BuildInfo(offeringBowl); HoverInfoCache[instanceID] = new HoverInfoCacheEntry { Info = text, ExpiresAt = realtimeSinceStartup + 0.25f }; return text; } private static string BuildInfo(OfferingBowl offeringBowl) { string spawnedText = GetSpawnedText(offeringBowl); string requiredText = GetRequiredText(offeringBowl); if (spawnedText.Length == 0) { return requiredText; } if (requiredText.Length == 0) { return spawnedText; } return spawnedText + "\n" + requiredText; } private static string GetSpawnedText(OfferingBowl offeringBowl) { if ((Object)(object)offeringBowl.m_bossPrefab != (Object)null) { return GetCharacterDisplayName(offeringBowl.m_bossPrefab); } if (!((Object)(object)offeringBowl.m_itemPrefab != (Object)null)) { return ""; } return GetItemDisplayName(offeringBowl.m_itemPrefab); } private static string GetRequiredText(OfferingBowl offeringBowl) { if (offeringBowl.m_useItemStands) { return GetRequiredTextFromItemStands(offeringBowl); } if ((Object)(object)offeringBowl.m_bossItem == (Object)null) { return ""; } string itemDisplayName = GetItemDisplayName(offeringBowl.m_bossItem); if (itemDisplayName.Length == 0) { return ""; } int num = Math.Max(1, offeringBowl.m_bossItems); if (num <= 1) { return itemDisplayName; } return $"{itemDisplayName} x{num}"; } private static string GetRequiredTextFromItemStands(OfferingBowl offeringBowl) { Dictionary dictionary = new Dictionary(StringComparer.Ordinal); foreach (ItemStand displayRelevantItemStand in AltarItemStandHoverInfoFormatter.GetDisplayRelevantItemStands(offeringBowl)) { if (!((Object)(object)displayRelevantItemStand == (Object)null) && displayRelevantItemStand.m_supportedItems != null && displayRelevantItemStand.m_supportedItems.Count == 1) { string itemDisplayName = GetItemDisplayName(displayRelevantItemStand.m_supportedItems[0]); if (itemDisplayName.Length != 0) { dictionary[itemDisplayName] = ((!dictionary.TryGetValue(itemDisplayName, out var value)) ? 1 : (value + 1)); } } } if (dictionary.Count != 0) { return string.Join(", ", dictionary.Select((KeyValuePair pair) => (pair.Value <= 1) ? pair.Key : $"{pair.Key} x{pair.Value}")); } return ""; } private static string GetCharacterDisplayName(GameObject prefab) { Character val = default(Character); if (!prefab.TryGetComponent(ref val)) { return Localize(((Object)prefab).name); } return Localize(string.IsNullOrWhiteSpace(val.m_name) ? ((Object)prefab).name : val.m_name); } internal static string GetItemDisplayName(ItemDrop itemDrop) { return Localize(itemDrop.m_itemData.m_shared.m_name); } private static string Localize(string text) { string text2; if (Localization.instance == null) { text2 = text; if (text2 == null) { return ""; } } else { text2 = Localization.instance.Localize(text ?? ""); } return text2; } private static void CleanupRegisteredOfferingBowls() { for (int num = RegisteredOfferingBowls.Count - 1; num >= 0; num--) { if (!((Object)(object)RegisteredOfferingBowls[num] != (Object)null)) { RegisteredOfferingBowls.RemoveAt(num); } } RegisteredOfferingBowlIds.Clear(); foreach (OfferingBowl registeredOfferingBowl in RegisteredOfferingBowls) { if ((Object)(object)registeredOfferingBowl != (Object)null) { RegisteredOfferingBowlIds.Add(((Object)registeredOfferingBowl).GetInstanceID()); } } } } internal static class AltarItemStandHoverInfoFormatter { private sealed class RelevantOfferingBowlCacheEntry { public OfferingBowl? OfferingBowl { get; set; } public float ExpiresAt { get; set; } } private sealed class HoverInfoCacheEntry { public string Info { get; set; } = ""; public float ExpiresAt { get; set; } } private const float RelevantOfferingBowlCacheSeconds = 1f; private const float HoverInfoCacheSeconds = 0.25f; private static readonly ItemStand[] EmptyItemStands = Array.Empty(); private static readonly List RegisteredItemStands = new List(); private static readonly HashSet RegisteredItemStandIds = new HashSet(); private static readonly Dictionary RelevantOfferingBowlCache = new Dictionary(); private static readonly Dictionary HoverInfoCache = new Dictionary(); internal static string AppendInfo(string baseText, ItemStand? itemStand) { if (!BossRulesConfig.ShouldShowOfferingBowlHoverInfo() || (Object)(object)itemStand == (Object)null) { return baseText ?? ""; } if ((Object)(object)((Component)itemStand).GetComponentInParent() != (Object)null) { return baseText ?? ""; } if (!TryGetRelevantOfferingBowl(itemStand, out OfferingBowl _)) { return baseText ?? ""; } string cachedInfo = GetCachedInfo(itemStand); if (cachedInfo.Length == 0) { return baseText ?? ""; } if (!string.IsNullOrWhiteSpace(baseText)) { return baseText + "\n" + cachedInfo; } return cachedInfo; } internal static void RegisterItemStand(ItemStand? itemStand) { if (!((Object)(object)itemStand == (Object)null) && RegisteredItemStandIds.Add(((Object)itemStand).GetInstanceID())) { RegisteredItemStands.Add(itemStand); } } internal static void ClearRuntimeCaches() { RelevantOfferingBowlCache.Clear(); HoverInfoCache.Clear(); OfferingBowlHoverInfoFormatter.ClearRuntimeCaches(); } internal static IReadOnlyList FindRelevantItemStands(OfferingBowl offeringBowl) { if ((Object)(object)offeringBowl == (Object)null || !offeringBowl.m_useItemStands) { return EmptyItemStands; } EnsureRegistryPopulated(); CleanupRegisteredItemStands(); List list = new List(); HashSet hashSet = new HashSet(); if (TryGetOfferingBowlStructuralRoot(offeringBowl, out Transform root) && (Object)(object)root != (Object)null) { AddRelevantItemStands(root, offeringBowl, list, hashSet); } foreach (ItemStand registeredItemStand in RegisteredItemStands) { if (!((Object)(object)registeredItemStand == (Object)null) && hashSet.Add(((Object)registeredItemStand).GetInstanceID()) && IsRelevantToOfferingBowl(registeredItemStand, offeringBowl)) { list.Add(registeredItemStand); } } if (list.Count != 0) { return list; } return EmptyItemStands; } internal static IReadOnlyList GetDisplayRelevantItemStands(OfferingBowl offeringBowl) { //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0033: 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_0049: 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) IReadOnlyList readOnlyList = FindRelevantItemStands(offeringBowl); if (readOnlyList.Count <= 1) { return readOnlyList; } Dictionary<(int, int, int), ItemStand> dictionary = new Dictionary<(int, int, int), ItemStand>(); foreach (ItemStand item in readOnlyList) { Vector3 position = ((Component)item).transform.position; (int, int, int) key = ((int)Math.Round(position.x * 20f), (int)Math.Round(position.y * 20f), (int)Math.Round(position.z * 20f)); if (!dictionary.TryGetValue(key, out var value) || GetDisplayPriority(item) > GetDisplayPriority(value)) { dictionary[key] = item; } } if (dictionary.Count != 0) { return dictionary.Values.ToList(); } return EmptyItemStands; } internal static bool TryGetRelevantOfferingBowl(ItemStand itemStand, out OfferingBowl? offeringBowl) { offeringBowl = null; if ((Object)(object)itemStand == (Object)null) { return false; } int instanceID = ((Object)itemStand).GetInstanceID(); float realtimeSinceStartup = Time.realtimeSinceStartup; if (RelevantOfferingBowlCache.TryGetValue(instanceID, out RelevantOfferingBowlCacheEntry value) && value.ExpiresAt > realtimeSinceStartup) { offeringBowl = value.OfferingBowl; return (Object)(object)offeringBowl != (Object)null; } offeringBowl = FindRelevantOfferingBowlUncached(itemStand); RelevantOfferingBowlCache[instanceID] = new RelevantOfferingBowlCacheEntry { OfferingBowl = offeringBowl, ExpiresAt = realtimeSinceStartup + 1f }; return (Object)(object)offeringBowl != (Object)null; } private static OfferingBowl? FindRelevantOfferingBowlUncached(ItemStand itemStand) { Location componentInParent = ((Component)itemStand).GetComponentInParent(true); if ((Object)(object)componentInParent != (Object)null) { return FindNearestRelevantOfferingBowl(itemStand, ((Component)componentInParent).GetComponentsInChildren(true)); } if (TryGetDetachedStructureRoot(((Component)itemStand).transform, out Transform root) && (Object)(object)root != (Object)null) { OfferingBowl val = FindNearestRelevantOfferingBowl(itemStand, ((Component)root).GetComponentsInChildren(true)); if ((Object)(object)val != (Object)null) { return val; } } return FindNearestRelevantOfferingBowl(itemStand, OfferingBowlHoverInfoFormatter.GetKnownOfferingBowls()); } internal static bool TryResolveOfferingBowlContext(OfferingBowl? offeringBowl, out string locationPrefab, out Transform root) { //IL_006d: Unknown result type (might be due to invalid IL or missing references) locationPrefab = ""; root = null; if ((Object)(object)offeringBowl == (Object)null) { return false; } root = ((Component)offeringBowl).transform; Location componentInParent = ((Component)offeringBowl).GetComponentInParent(true); Transform root2; if ((Object)(object)componentInParent != (Object)null) { root = ((Component)componentInParent).transform; } else if (TryGetDetachedStructureRoot(((Component)offeringBowl).transform, out root2) && (Object)(object)root2 != (Object)null) { root = root2; } if (AltarLocationResolver.TryResolveLocationPrefabName(componentInParent, out locationPrefab)) { return locationPrefab.Length > 0; } if (AltarLocationResolver.TryResolveZoneLocationPrefabName(((Component)offeringBowl).transform.position, out locationPrefab)) { return true; } LocationProxy componentInParent2 = ((Component)offeringBowl).GetComponentInParent(true); if ((Object)(object)componentInParent2 != (Object)null) { return AltarLocationResolver.TryResolveLocationProxyPrefabName(componentInParent2, out locationPrefab); } return false; } internal static bool IsRelevantToOfferingBowl(ItemStand? itemStand, OfferingBowl? offeringBowl) { //IL_0022: 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) if ((Object)(object)itemStand == (Object)null || (Object)(object)offeringBowl == (Object)null || !offeringBowl.m_useItemStands) { return false; } if (Vector3.Distance(((Component)offeringBowl).transform.position, ((Component)itemStand).transform.position) > offeringBowl.m_itemstandMaxRange) { return false; } return ((Object)((Component)itemStand).gameObject).name.StartsWith(offeringBowl.m_itemStandPrefix ?? "", StringComparison.OrdinalIgnoreCase); } internal static bool TryGetDetachedStructureRoot(Transform transform, out Transform? root) { if ((Object)(object)transform == (Object)null) { root = null; return false; } Transform val = transform; while ((Object)(object)val.parent != (Object)null) { Transform parent = val.parent; if ((Object)(object)((Component)parent).GetComponent() != (Object)null || (Object)(object)((Component)parent).GetComponent() != (Object)null || string.Equals(((Object)parent).name, "_ZoneCtrl(Clone)", StringComparison.Ordinal)) { break; } val = parent; } root = val; return (Object)(object)root != (Object)null; } private static string BuildInfo(ItemStand itemStand) { if (itemStand.m_supportedItems == null || itemStand.m_supportedItems.Count == 0) { return ""; } return string.Join(", ", (from name in itemStand.m_supportedItems.Where((ItemDrop item) => (Object)(object)item != (Object)null).Select(OfferingBowlHoverInfoFormatter.GetItemDisplayName) where !string.IsNullOrWhiteSpace(name) select name).Distinct(StringComparer.Ordinal)); } private static string GetCachedInfo(ItemStand itemStand) { int instanceID = ((Object)itemStand).GetInstanceID(); float realtimeSinceStartup = Time.realtimeSinceStartup; if (HoverInfoCache.TryGetValue(instanceID, out HoverInfoCacheEntry value) && value.ExpiresAt > realtimeSinceStartup) { return value.Info; } string text = BuildInfo(itemStand); HoverInfoCache[instanceID] = new HoverInfoCacheEntry { Info = text, ExpiresAt = realtimeSinceStartup + 0.25f }; return text; } private static void AddRelevantItemStands(Transform root, OfferingBowl offeringBowl, List itemStands, HashSet seenIds) { ItemStand[] componentsInChildren = ((Component)root).GetComponentsInChildren(true); foreach (ItemStand val in componentsInChildren) { if (!((Object)(object)val == (Object)null) && seenIds.Add(((Object)val).GetInstanceID()) && IsRelevantToOfferingBowl(val, offeringBowl)) { itemStands.Add(val); } } } private static OfferingBowl? FindNearestRelevantOfferingBowl(ItemStand itemStand, IEnumerable candidates) { //IL_0030: 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_0040: Unknown result type (might be due to invalid IL or missing references) OfferingBowl result = null; float num = float.MaxValue; foreach (OfferingBowl candidate in candidates) { if (!((Object)(object)candidate == (Object)null) && IsRelevantToOfferingBowl(itemStand, candidate)) { float num2 = Vector3.SqrMagnitude(((Component)candidate).transform.position - ((Component)itemStand).transform.position); if (!(num2 >= num)) { num = num2; result = candidate; } } } return result; } private static bool TryGetOfferingBowlStructuralRoot(OfferingBowl offeringBowl, out Transform? root) { Location componentInParent = ((Component)offeringBowl).GetComponentInParent(true); root = ((componentInParent != null) ? ((Component)componentInParent).transform : null); if (!((Object)(object)root != (Object)null)) { return TryGetDetachedStructureRoot(((Component)offeringBowl).transform, out root); } return true; } private static int GetDisplayPriority(ItemStand itemStand) { int num = (((Component)itemStand).gameObject.activeInHierarchy ? 4 : 0); if ((Object)(object)((Component)itemStand).GetComponentInParent(true) == (Object)null) { num += 2; } return num + (itemStand.m_supportedItems?.Count ?? 0); } private static void EnsureRegistryPopulated() { if (RegisteredItemStands.Count <= 0) { ItemStand[] array = Object.FindObjectsByType((FindObjectsSortMode)0); for (int i = 0; i < array.Length; i++) { RegisterItemStand(array[i]); } } } private static void CleanupRegisteredItemStands() { for (int num = RegisteredItemStands.Count - 1; num >= 0; num--) { if (!((Object)(object)RegisteredItemStands[num] != (Object)null)) { RegisteredItemStands.RemoveAt(num); } } RegisteredItemStandIds.Clear(); foreach (ItemStand registeredItemStand in RegisteredItemStands) { if ((Object)(object)registeredItemStand != (Object)null) { RegisteredItemStandIds.Add(((Object)registeredItemStand).GetInstanceID()); } } } } internal static class BossRulesDebugLog { internal static bool IsClientEnabled => BossRulesConfig.IsClientDebugLogEnabled(); internal static void Client(string message) { if (IsClientEnabled) { BossRulesPlugin.BossRulesLogger.LogInfo((object)("[ClientDebug] " + message)); } } internal static string FormatVector3(Vector3 position) { //IL_000a: 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_0020: Unknown result type (might be due to invalid IL or missing references) return string.Format(CultureInfo.InvariantCulture, "{0:0.##},{1:0.##},{2:0.##}", position.x, position.y, position.z); } internal static string FormatRefunds(IReadOnlyCollection? refunds) { if (refunds == null || refunds.Count == 0) { return ""; } return string.Join(", ", from refund in refunds where (Object)(object)refund?.Prefab != (Object)null && refund.Amount > 0 select (!refund.DropPointOverride.HasValue) ? $"{((Object)refund.Prefab).name}:{refund.Amount}" : $"{((Object)refund.Prefab).name}:{refund.Amount}@{FormatVector3(refund.DropPointOverride.Value)}"); } } internal static class BossRulesConfig { private const float SameBossDuplicateBlockRadius = 64f; private static ConfigEntry _clientDebugLog; private static ConfigEntry _enableAltarRules; private static ConfigEntry _showOfferingBowlHoverInfo; private static ConfigEntry _enableSameBossDuplicateBlock; private static ConfigEntry _enableDespawnRules; private static ConfigEntry _enableBossTamedPressure; private static ConfigEntry _enableForsakenPowerRules; private static ConfigEntry _guardianPowerActivationAdrenaline; private static ConfigEntry _perPlayerBossStones; private static ConfigEntry _remoteForsakenPowerSelection; private static ConfigEntry _rotateForsakenPowerShortcut; internal static void Bind(BossRulesPlugin plugin) { //IL_0035: Unknown result type (might be due to invalid IL or missing references) _clientDebugLog = plugin.BindConfigEntry("1 - General", "Client Debug Log", BossRulesPlugin.Toggle.Off, "If on, writes local BossRules diagnostic logs for this machine. Useful for altar refund and runtime tracing.", synchronizedSetting: false, 100); _rotateForsakenPowerShortcut = plugin.BindConfigEntry("2 - Forsaken & Altars", "Rotate Forsaken Power Shortcut", new KeyboardShortcut((KeyCode)103, Array.Empty()), "Shortcut used to rotate through unlocked Forsaken Powers when Remote Forsaken Power Selection is enabled. This setting is client-side only.", synchronizedSetting: false, 600); _enableAltarRules = plugin.BindConfigEntry("2 - Forsaken & Altars", "Enable Altar Rules", BossRulesPlugin.Toggle.On, "If on, BossRules.altar.yml altar entries can override OfferingBowl and boss ItemStand behavior.", synchronizedSetting: true, 100); _showOfferingBowlHoverInfo = plugin.BindConfigEntry("2 - Forsaken & Altars", "Show OfferingBowl Hover Info", BossRulesPlugin.Toggle.On, "If on, looking at an OfferingBowl shows simplified offering info with the spawned boss/item and required offering item. Matching altar ItemStands also show their required supported item names.", synchronizedSetting: true, 200); _enableSameBossDuplicateBlock = plugin.BindConfigEntry("3 - Boss Rules", "Enable Same Boss Duplicate Block", BossRulesPlugin.Toggle.On, $"If on, OfferingBowls and CreatureSpawners block new boss spawns when the same boss prefab already exists within {64f:0} horizontal XZ meters. CreatureSpawner respawn timing starts after the duplicate boss is gone.", synchronizedSetting: true, 550); _enableBossTamedPressure = plugin.BindConfigEntry("3 - Boss Rules", "Enable Boss Tamed Pressure", BossRulesPlugin.Toggle.Off, "If on, BossRules.yml bossTamedPressure entries can damage and weaken tamed MonsterAI creatures near configured boss prefabs.", synchronizedSetting: true, 400); _perPlayerBossStones = plugin.BindConfigEntry("2 - Forsaken & Altars", "Personalized Boss Stones", BossRulesPlugin.Toggle.On, "If on, each player has their own boss stone unlock state. Players inside the boss stone location when a trophy is sacrificed unlock that Forsaken Power for themselves.", synchronizedSetting: true, 300); _enableForsakenPowerRules = plugin.BindConfigEntry("2 - Forsaken & Altars", "Forsaken Power Overhaul", BossRulesPlugin.Toggle.On, "If on, BossRules.forsakenPowers.yml entries can rebalance Forsaken Power duration, cooldown, stats, resistances, and supported special effects.", synchronizedSetting: true, 400); _guardianPowerActivationAdrenaline = plugin.BindConfigEntry("2 - Forsaken & Altars", "Guardian Power Activation Adrenaline", 1f, "Adrenaline granted when a Guardian Power is activated. Vanilla is 10. Negative values clamp to 0.", synchronizedSetting: true, 450); _remoteForsakenPowerSelection = plugin.BindConfigEntry("2 - Forsaken & Altars", "Remote Forsaken Power Selection", BossRulesPlugin.Toggle.On, "If on, players can rotate through Forsaken Powers they have unlocked through per-player boss stones without returning to the Start Temple.", synchronizedSetting: true, 500); _enableDespawnRules = plugin.BindConfigEntry("3 - Boss Rules", "Enable Despawn Rules", BossRulesPlugin.Toggle.On, "If on, BossRules.yml despawn entries and auto-detected boss despawn tracking can remove bosses when no living player is nearby.", synchronizedSetting: true, 300); } internal static bool IsClientDebugLogEnabled() { ConfigEntry clientDebugLog = _clientDebugLog; if (clientDebugLog == null) { return false; } return clientDebugLog.Value == BossRulesPlugin.Toggle.On; } internal static bool IsAltarRulesEnabled() { ConfigEntry enableAltarRules = _enableAltarRules; if (enableAltarRules == null) { return true; } return enableAltarRules.Value != BossRulesPlugin.Toggle.Off; } internal static bool ShouldShowOfferingBowlHoverInfo() { ConfigEntry showOfferingBowlHoverInfo = _showOfferingBowlHoverInfo; if (showOfferingBowlHoverInfo == null) { return true; } return showOfferingBowlHoverInfo.Value != BossRulesPlugin.Toggle.Off; } internal static float GetSameBossDuplicateBlockRadius() { ConfigEntry enableSameBossDuplicateBlock = _enableSameBossDuplicateBlock; if (enableSameBossDuplicateBlock == null || enableSameBossDuplicateBlock.Value != BossRulesPlugin.Toggle.Off) { return 64f; } return 0f; } internal static bool IsDespawnRulesEnabled() { ConfigEntry enableDespawnRules = _enableDespawnRules; if (enableDespawnRules == null) { return true; } return enableDespawnRules.Value != BossRulesPlugin.Toggle.Off; } internal static bool ShouldCaptureAltarSpawnRefunds() { return IsDespawnRulesEnabled(); } internal static bool IsBossTamedPressureEnabled() { ConfigEntry enableBossTamedPressure = _enableBossTamedPressure; if (enableBossTamedPressure == null) { return true; } return enableBossTamedPressure.Value != BossRulesPlugin.Toggle.Off; } internal static bool IsForsakenPowerRulesEnabled() { ConfigEntry enableForsakenPowerRules = _enableForsakenPowerRules; if (enableForsakenPowerRules == null) { return true; } return enableForsakenPowerRules.Value != BossRulesPlugin.Toggle.Off; } internal static float GetGuardianPowerActivationAdrenaline() { return Mathf.Max(0f, _guardianPowerActivationAdrenaline?.Value ?? 1f); } internal static bool IsPerPlayerBossStonesEnabled() { ConfigEntry perPlayerBossStones = _perPlayerBossStones; if (perPlayerBossStones == null) { return true; } return perPlayerBossStones.Value != BossRulesPlugin.Toggle.Off; } internal static bool IsRemoteForsakenPowerSelectionEnabled() { ConfigEntry remoteForsakenPowerSelection = _remoteForsakenPowerSelection; if (remoteForsakenPowerSelection == null) { return true; } return remoteForsakenPowerSelection.Value != BossRulesPlugin.Toggle.Off; } internal static KeyboardShortcut GetRotateForsakenPowerShortcut() { //IL_0013: 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_0011: Unknown result type (might be due to invalid IL or missing references) return (KeyboardShortcut)(((??)_rotateForsakenPowerShortcut?.Value) ?? default(KeyboardShortcut)); } } internal static class BossRulesManager { private static readonly Dictionary> TrackedBossesByPrefab = new Dictionary>(StringComparer.OrdinalIgnoreCase); internal static bool ShouldBlockConfiguredSameBossSpawn(GameObject? targetPrefab, Vector3 sourcePosition) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) return ShouldBlockSameBossSpawn(targetPrefab, sourcePosition, BossRulesConfig.GetSameBossDuplicateBlockRadius()); } internal static bool ShouldBlockSameBossSpawn(GameObject? targetPrefab, Vector3 sourcePosition, float radius) { //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_0082: 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 (radius <= 0f || !TryGetBossPrefabName(targetPrefab, out string prefabName)) { return false; } if (!TrackedBossesByPrefab.TryGetValue(prefabName, out HashSet value) || value.Count == 0) { return false; } float num = radius * radius; value.RemoveWhere((Character character) => !IsTrackableBossCharacter(character)); if (value.Count == 0) { TrackedBossesByPrefab.Remove(prefabName); return false; } foreach (Character item in value) { Vector3 val = item.GetCenterPoint() - sourcePosition; val.y = 0f; if (((Vector3)(ref val)).sqrMagnitude < num) { return true; } } return false; } internal static void TrackBossCharacter(Character? character) { if (TryGetTrackableBossPrefabName(character, out string prefabName)) { if (!TrackedBossesByPrefab.TryGetValue(prefabName, out HashSet value)) { value = new HashSet(); TrackedBossesByPrefab[prefabName] = value; } value.Add(character); } } internal static void UntrackBossCharacter(Character? character) { if ((Object)(object)character == (Object)null) { return; } if (TryGetTrackableBossPrefabName(character, out string prefabName) && TrackedBossesByPrefab.TryGetValue(prefabName, out HashSet value)) { value.Remove(character); if (value.Count == 0) { TrackedBossesByPrefab.Remove(prefabName); } return; } string text = null; foreach (KeyValuePair> item in TrackedBossesByPrefab) { if (item.Value.Remove(character)) { if (item.Value.Count == 0) { text = item.Key; } break; } } if (text != null) { TrackedBossesByPrefab.Remove(text); } } internal static void ClearRuntimeState() { TrackedBossesByPrefab.Clear(); CreatureSpawnerDuplicateBlockRuntime.ClearRuntimeState(); } private static bool TryGetTrackableBossPrefabName(Character? character, out string prefabName) { prefabName = ""; if (IsTrackableBossCharacter(character)) { return TryGetBossPrefabName(((Component)character).gameObject, out prefabName); } return false; } private static bool IsTrackableBossCharacter(Character? character) { if ((Object)(object)character != (Object)null && (Object)(object)((Component)character).gameObject != (Object)null && !character.IsDead()) { return character.IsBoss(); } return false; } private static bool TryGetBossPrefabName(GameObject? prefab, out string prefabName) { prefabName = AltarRuntime.GetPrefabName(prefab); Character val = default(Character); if ((Object)(object)prefab != (Object)null && prefabName.Length > 0 && prefab.TryGetComponent(ref val)) { return val.IsBoss(); } return false; } } internal static class BossRulesRuntime { private sealed class CompiledDespawnRule { public float? RangeOverride { get; set; } public float? DelayOverride { get; set; } public bool RefundsEnabled { get; set; } = true; } private sealed class RuntimeState { public static RuntimeState Empty { get; } = new RuntimeState(); public Dictionary RulesByPrefab { get; } = new Dictionary(StringComparer.OrdinalIgnoreCase); public Dictionary RulesByPrefabHash { get; } = new Dictionary(); public Dictionary PrefabNamesByHash { get; } = new Dictionary(); public HashSet EligiblePrefabHashes { get; } = new HashSet(); public HashSet BootstrapPrefabs { get; } = new HashSet(StringComparer.OrdinalIgnoreCase); public IReadOnlyList BootstrapPrefabOrder { get; set; } = Array.Empty(); } private sealed class BossCatalog { public static BossCatalog Empty { get; } = new BossCatalog(); public int GameDataSignature { get; set; } = -1; public HashSet BossPrefabNames { get; } = new HashSet(StringComparer.OrdinalIgnoreCase); public HashSet BossPrefabHashes { get; } = new HashSet(); } private static readonly object Sync = new object(); private static RuntimeState _runtimeState = RuntimeState.Empty; private static BossCatalog _bossCatalog = BossCatalog.Empty; private static int _runtimeGameDataSignature = -1; private static int _runtimeConfigurationVersion = -1; private static int _configurationVersion; private static int _cachedGameDataMarker = int.MinValue; private static int _cachedGameDataSignature = -1; private static bool _cachedGameDataSignatureDirty = true; private static BossRuleConfigurationState _configuration = BossRuleConfigurationState.Empty; private static int _despawnLookupVersion; internal static void Reload(BossRuleConfigurationState configuration) { lock (Sync) { _configuration = configuration ?? BossRuleConfigurationState.Empty; _runtimeState = RuntimeState.Empty; _runtimeGameDataSignature = -1; _runtimeConfigurationVersion = -1; _configurationVersion++; _cachedGameDataSignatureDirty = true; _despawnLookupVersion++; DespawnRulesManager.ConfigureDefaults(_configuration.DefaultDespawnRange, _configuration.DefaultDespawnDelaySeconds); DespawnRulesManager.MarkBootstrapScanDirty("boss rules reload"); DespawnRulesManager.ConfigureMessages(_configuration.MessageDespawnStart, _configuration.MessageDespawnReminder, _configuration.MessageDespawnCanceled); BossTamedPressureRuntime.Configure(_configuration.BossTamedPressureRules, _configuration.MessageBossTamedPressure); ForsakenPowerRuntime.Configure(_configuration.ForsakenPowers); } } internal static void Reset() { lock (Sync) { _configuration = BossRuleConfigurationState.Empty; _runtimeState = RuntimeState.Empty; _runtimeGameDataSignature = -1; _runtimeConfigurationVersion = -1; _configurationVersion++; _cachedGameDataSignatureDirty = true; _bossCatalog = BossCatalog.Empty; _despawnLookupVersion++; DespawnRulesManager.ConfigureDefaults(64f, 90f); DespawnRulesManager.ConfigureMessages(null, null, null); BossTamedPressureRuntime.Configure(Array.Empty(), null); ForsakenPowerRuntime.Reset(); } } internal static string GetForsakenPowerRotateLabel() { lock (Sync) { string text = _configuration.MessageForsakenPowerRotate ?? ""; return (text.Length > 0) ? text : "Rotate"; } } internal static int GetDespawnLookupVersion() { EnsureRuntimeState(); return _despawnLookupVersion; } internal static IReadOnlyList GetDespawnBootstrapPrefabOrder() { EnsureRuntimeState(); return _runtimeState.BootstrapPrefabOrder; } internal static bool IsDespawnTrackingRuleLookupReady() { EnsureRuntimeState(); return _runtimeGameDataSignature >= 0; } internal static bool TryGetCachedDespawnTrackingPrefabHashEligibility(int prefabHash, out bool eligible) { eligible = false; if (prefabHash == 0 || _runtimeGameDataSignature < 0) { return false; } eligible = _runtimeState.EligiblePrefabHashes.Contains(prefabHash); return true; } internal static bool IsEligibleDespawnTrackingPrefabHash(int prefabHash) { if (prefabHash == 0) { return false; } EnsureRuntimeState(); return _runtimeState.EligiblePrefabHashes.Contains(prefabHash); } internal static bool IsEligibleDespawnTrackingPrefabName(string prefabName) { if (string.IsNullOrWhiteSpace(prefabName)) { return false; } EnsureRuntimeState(); return _runtimeState.BootstrapPrefabs.Contains(prefabName); } internal static bool TryResolveDespawnTrackingRule(string prefabName, out float? rangeOverride, out float? delayOverride, out IReadOnlyCollection refunds) { rangeOverride = null; delayOverride = null; refunds = (IReadOnlyCollection)(object)Array.Empty(); if (string.IsNullOrWhiteSpace(prefabName)) { return false; } EnsureRuntimeState(); if (_runtimeState.RulesByPrefab.TryGetValue(prefabName, out CompiledDespawnRule value)) { rangeOverride = value.RangeOverride; delayOverride = value.DelayOverride; refunds = ResolveRefundsForRule(value, null); return true; } return IsAutoDetectedBossPrefab(prefabName); } internal static bool TryResolveDespawnTrackingRule(int prefabHash, out string prefabName, out float? rangeOverride, out float? delayOverride, out IReadOnlyCollection refunds) { prefabName = ""; rangeOverride = null; delayOverride = null; refunds = (IReadOnlyCollection)(object)Array.Empty(); if (prefabHash == 0) { return false; } EnsureRuntimeState(); if (!_runtimeState.PrefabNamesByHash.TryGetValue(prefabHash, out prefabName) || string.IsNullOrWhiteSpace(prefabName)) { prefabName = ResolvePrefabName(prefabHash); } if (_runtimeState.RulesByPrefabHash.TryGetValue(prefabHash, out CompiledDespawnRule value)) { rangeOverride = value.RangeOverride; delayOverride = value.DelayOverride; refunds = ResolveRefundsForRule(value, null); return !string.IsNullOrWhiteSpace(prefabName); } if (IsAutoDetectedBossPrefab(prefabHash)) { return !string.IsNullOrWhiteSpace(prefabName); } return false; } internal static bool TryResolveDespawnTrackingRule(ZDO zdo, int prefabHashHint, string prefabNameHint, out string prefabName, out float? rangeOverride, out float? delayOverride, out IReadOnlyCollection refunds) { prefabName = (string.IsNullOrWhiteSpace(prefabNameHint) ? "" : prefabNameHint); rangeOverride = null; delayOverride = null; refunds = (IReadOnlyCollection)(object)Array.Empty(); int num = zdo.GetPrefab(); if (num == 0) { num = prefabHashHint; } EnsureRuntimeState(); if (num != 0) { if (!_runtimeState.PrefabNamesByHash.TryGetValue(num, out string value) || string.IsNullOrWhiteSpace(value)) { value = ResolvePrefabName(num); } if (!string.IsNullOrWhiteSpace(value)) { prefabName = value; } if (_runtimeState.RulesByPrefabHash.TryGetValue(num, out CompiledDespawnRule value2)) { rangeOverride = value2.RangeOverride; delayOverride = value2.DelayOverride; refunds = ResolveRefundsForRule(value2, zdo); return !string.IsNullOrWhiteSpace(prefabName); } if (IsAutoDetectedBossPrefab(num)) { refunds = ResolveAutoAltarRefunds(zdo); return !string.IsNullOrWhiteSpace(prefabName); } } if (!string.IsNullOrWhiteSpace(prefabName) && _runtimeState.RulesByPrefab.TryGetValue(prefabName, out CompiledDespawnRule value3)) { rangeOverride = value3.RangeOverride; delayOverride = value3.DelayOverride; refunds = ResolveRefundsForRule(value3, zdo); return true; } if (!string.IsNullOrWhiteSpace(prefabName) && IsAutoDetectedBossPrefab(prefabName)) { refunds = ResolveAutoAltarRefunds(zdo); return true; } return false; } internal static bool IsAutoDetectedBossPrefab(string prefabName) { if (string.IsNullOrWhiteSpace(prefabName)) { return false; } return EnsureBossCatalog().BossPrefabNames.Contains(prefabName); } internal static bool IsAutoDetectedBossPrefab(int prefabHash) { if (prefabHash == 0) { return false; } return EnsureBossCatalog().BossPrefabHashes.Contains(prefabHash); } internal static IReadOnlyCollection GetAutoDetectedBossPrefabNames() { return EnsureBossCatalog().BossPrefabNames; } internal static int ComputeGameDataSignature() { int num = 17; foreach (GameObject item in EnumeratePrefabsForRuntime()) { if (!((Object)(object)item == (Object)null)) { string prefabNameForRuntime = GetPrefabNameForRuntime(item); if (prefabNameForRuntime.Length != 0) { num = num * 31 + StringExtensionMethods.GetStableHashCode(prefabNameForRuntime); } } } return num; } internal static int GetGameDataSignature() { int gameDataMarker = GetGameDataMarker(); if (!_cachedGameDataSignatureDirty && _cachedGameDataMarker == gameDataMarker) { return _cachedGameDataSignature; } _cachedGameDataMarker = gameDataMarker; _cachedGameDataSignature = ComputeGameDataSignature(); _cachedGameDataSignatureDirty = false; return _cachedGameDataSignature; } internal static IEnumerable EnumeratePrefabsForRuntime() { HashSet seen = new HashSet(); if (ZNetScene.instance?.m_prefabs != null) { foreach (GameObject prefab in ZNetScene.instance.m_prefabs) { if ((Object)(object)prefab != (Object)null && seen.Add(prefab)) { yield return prefab; } } } if (ObjectDB.instance?.m_items == null) { yield break; } foreach (GameObject item in ObjectDB.instance.m_items) { if ((Object)(object)item != (Object)null && seen.Add(item)) { yield return item; } } } internal static string GetPrefabNameForRuntime(GameObject? prefab) { if ((Object)(object)prefab == (Object)null) { return ""; } string prefabName = Utils.GetPrefabName(prefab); if (!string.IsNullOrWhiteSpace(prefabName)) { return prefabName; } return TrimCloneSuffix(((Object)prefab).name); } internal static bool TryResolveItemPrefab(string itemName, string context, out GameObject prefab) { prefab = null; string text = (itemName ?? "").Trim(); if (text.Length == 0) { WarnInvalidEntry("Entry '" + context + "' contains an empty item prefab name."); return false; } ObjectDB instance = ObjectDB.instance; object obj = ((instance != null) ? instance.GetItemPrefab(text) : null); if (obj == null) { ZNetScene instance2 = ZNetScene.instance; obj = ((instance2 != null) ? instance2.GetPrefab(text) : null); } GameObject val = (GameObject)obj; if ((Object)(object)val == (Object)null) { WarnInvalidEntry("Entry '" + context + "' references unknown item prefab '" + text + "'."); return false; } ItemDrop val2 = default(ItemDrop); if (!val.TryGetComponent(ref val2)) { WarnInvalidEntry("Entry '" + context + "' references '" + text + "', but it is not an item prefab."); return false; } prefab = val; return true; } internal static void WarnInvalidEntry(string message) { if (!string.IsNullOrWhiteSpace(message)) { BossRulesPlugin.BossRulesLogger.LogWarning((object)message); } } private static IReadOnlyCollection ResolveRefundsForRule(CompiledDespawnRule rule, ZDO? zdo) { if (!rule.RefundsEnabled) { return (IReadOnlyCollection)(object)Array.Empty(); } return ResolveAutoAltarRefunds(zdo); } private static IReadOnlyCollection ResolveAutoAltarRefunds(ZDO? zdo) { if (zdo == null || !AltarRuntime.TryResolveAltarSummonRefunds(zdo, out IReadOnlyCollection refunds)) { return (IReadOnlyCollection)(object)Array.Empty(); } return refunds; } private static void EnsureRuntimeState() { int gameDataSignature = GetGameDataSignature(); if (_runtimeGameDataSignature != gameDataSignature || _runtimeConfigurationVersion != _configurationVersion) { _runtimeState = BuildRuntimeState(_configuration); _runtimeGameDataSignature = gameDataSignature; _runtimeConfigurationVersion = _configurationVersion; _despawnLookupVersion++; } } private static RuntimeState BuildRuntimeState(BossRuleConfigurationState configuration) { RuntimeState runtimeState = new RuntimeState(); foreach (string autoDetectedBossPrefabName in GetAutoDetectedBossPrefabNames()) { runtimeState.BootstrapPrefabs.Add(autoDetectedBossPrefabName); int stableHashCode = StringExtensionMethods.GetStableHashCode(autoDetectedBossPrefabName); runtimeState.PrefabNamesByHash[stableHashCode] = autoDetectedBossPrefabName; runtimeState.EligiblePrefabHashes.Add(stableHashCode); } foreach (BossDespawnDefinition despawnRule in configuration.DespawnRules) { if (!string.IsNullOrWhiteSpace(despawnRule.Prefab)) { CompiledDespawnRule value = new CompiledDespawnRule { RangeOverride = despawnRule.DespawnRange, DelayOverride = despawnRule.DespawnDelay, RefundsEnabled = (despawnRule.Refunds != false) }; string text = despawnRule.Prefab.Trim(); if (runtimeState.RulesByPrefab.ContainsKey(text)) { WarnInvalidEntry("Character prefab '" + text + "' defines multiple despawn rules. The later entry overrides the earlier entry."); } int stableHashCode2 = StringExtensionMethods.GetStableHashCode(text); runtimeState.RulesByPrefab[text] = value; runtimeState.RulesByPrefabHash[stableHashCode2] = value; runtimeState.PrefabNamesByHash[stableHashCode2] = text; runtimeState.EligiblePrefabHashes.Add(stableHashCode2); runtimeState.BootstrapPrefabs.Add(text); } } runtimeState.BootstrapPrefabOrder = runtimeState.BootstrapPrefabs.OrderBy((string prefabName) => prefabName, StringComparer.OrdinalIgnoreCase).ToArray(); return runtimeState; } private static BossCatalog EnsureBossCatalog() { int gameDataSignature = GetGameDataSignature(); if (_bossCatalog.GameDataSignature == gameDataSignature) { return _bossCatalog; } BossCatalog bossCatalog = new BossCatalog { GameDataSignature = gameDataSignature }; Character val = default(Character); foreach (GameObject item in EnumeratePrefabsForRuntime()) { if (!((Object)(object)item == (Object)null) && item.TryGetComponent(ref val) && val.IsBoss()) { string prefabNameForRuntime = GetPrefabNameForRuntime(item); if (prefabNameForRuntime.Length != 0) { bossCatalog.BossPrefabNames.Add(prefabNameForRuntime); bossCatalog.BossPrefabHashes.Add(StringExtensionMethods.GetStableHashCode(prefabNameForRuntime)); } } } _bossCatalog = bossCatalog; return _bossCatalog; } private static string ResolvePrefabName(int prefabHash) { if (prefabHash == 0 || (Object)(object)ZNetScene.instance == (Object)null) { return ""; } GameObject prefab = ZNetScene.instance.GetPrefab(prefabHash); if (!((Object)(object)prefab != (Object)null)) { return ""; } return ((Object)prefab).name; } private static int GetGameDataMarker() { ZNetScene instance = ZNetScene.instance; int num = (17 * 31 + (((Object)(object)instance != (Object)null) ? ((Object)instance).GetInstanceID() : 0)) * 31 + (instance?.m_prefabs?.Count ?? (-1)); ObjectDB instance2 = ObjectDB.instance; return (num * 31 + (((Object)(object)instance2 != (Object)null) ? ((Object)instance2).GetInstanceID() : 0)) * 31 + (instance2?.m_items?.Count ?? (-1)); } private static string TrimCloneSuffix(string? name) { string text = (name ?? "").Trim(); if (!text.EndsWith("(Clone)", StringComparison.Ordinal)) { return text; } return text.Substring(0, text.Length - "(Clone)".Length).TrimEnd(Array.Empty()); } } internal static class BossStonePerPlayerRuntime { private sealed class PendingBossStoneResetRequest { public long RequestId { get; set; } public long RequesterPeerId { get; set; } public string TargetPlayerName { get; set; } = ""; public float CreatedAt { get; set; } public float NextRetryAt { get; set; } } private const string StartTemplePrefabName = "StartTemple"; private const string DeepNorthBossStonesPrefabName = "DeepNorth_BossStones_TW"; private const string PlayerKeyPrefix = "dns_bossstone_"; private const string BossStoneSacrificeRequestRpc = "BossRules BossStone Sacrifice Request"; private const string BossStoneResetRequestRpc = "BossRules BossStone Reset Request"; private const string BossStoneResetApplyRpc = "BossRules BossStone Reset Apply"; private const string BossStoneResetAckRpc = "BossRules BossStone Reset Ack"; private const string BossStoneResetStatusRpc = "BossRules BossStone Reset Status"; private const float BossStoneResetRetryIntervalSeconds = 0.5f; private const float BossStoneResetRequestTimeoutSeconds = 10f; private static readonly HashSet PerPlayerBossStoneLocationPrefabs = new HashSet(StringComparer.OrdinalIgnoreCase) { "StartTemple", "DeepNorth_BossStones_TW" }; private static readonly FieldRef ItemStandNviewRef = AccessTools.FieldRefAccess("m_nview"); private static readonly FieldRef RoutedRpcIdRef = AccessTools.FieldRefAccess("m_id"); private static readonly MethodInfo? ItemStandGetOrientationMethod = AccessTools.Method(typeof(ItemStand), "GetOrientation", (Type[])null, (Type[])null); private static readonly MethodInfo? ItemStandSetVisualItemMethod = AccessTools.Method(typeof(ItemStand), "SetVisualItem", new Type[4] { typeof(string), typeof(int), typeof(int), typeof(int) }, (Type[])null); private static ZRoutedRpc? _registeredRpcInstance; private static readonly Dictionary PendingBossStoneResetRequests = new Dictionary(); private static long _nextBossStoneSacrificeRequestId = 1L; private static long _nextBossStoneResetRequestId = 1L; internal static void Initialize() { EnsureRpcRegistered(); } internal static void EnsureRpcRegistered() { ZRoutedRpc instance = ZRoutedRpc.instance; if (instance != null && instance != _registeredRpcInstance) { if (_registeredRpcInstance != null) { Shutdown(); } instance.Register("BossRules BossStone Sacrifice Request", (Action)OnBossStoneSacrificeRequestRpc); instance.Register("BossRules BossStone Reset Request", (Action)OnBossStoneResetRequestRpc); instance.Register("BossRules BossStone Reset Apply", (Action)OnBossStoneResetApplyRpc); instance.Register("BossRules BossStone Reset Ack", (Action)OnBossStoneResetAckRpc); instance.Register("BossRules BossStone Reset Status", (Action)OnBossStoneResetStatusRpc); _registeredRpcInstance = instance; } } internal static void Shutdown() { _registeredRpcInstance = null; PendingBossStoneResetRequests.Clear(); } internal static bool ShouldHandle(ItemStand? itemStand) { string playerKey; return TryResolveBossStoneHandle(itemStand, out playerKey); } internal static bool TryRequestReset(string exactPlayerName, out string message) { if (!TryResolveKnownPlayerName(exactPlayerName, out string resolvedPlayerName)) { string text = (exactPlayerName ?? "").Trim(); message = ((text.Length == 0) ? "Syntax: bossrules:bossstone reset " : ("Player '" + text + "' not found. Use exact player name.")); return false; } EnsureRpcRegistered(); if (ZRoutedRpc.instance == null) { message = "Boss stone reset is unavailable because routed RPC is not ready."; return false; } ZRoutedRpc.instance.InvokeRoutedRPC("BossRules BossStone Reset Request", new object[1] { resolvedPlayerName }); message = "Queued boss stone reset request for '" + resolvedPlayerName + "'. Awaiting server result."; return true; } internal static void ProcessPendingResetRequests() { if (ZRoutedRpc.instance == null || (Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer() || PendingBossStoneResetRequests.Count == 0) { return; } float realtimeSinceStartup = Time.realtimeSinceStartup; long[] array = PendingBossStoneResetRequests.Keys.ToArray(); foreach (long num in array) { if (!PendingBossStoneResetRequests.TryGetValue(num, out PendingBossStoneResetRequest value) || value == null || realtimeSinceStartup < value.NextRetryAt) { continue; } if (realtimeSinceStartup - value.CreatedAt >= 10f) { CompletePendingBossStoneResetRequest(num, $"Boss stone reset failed for '{value.TargetPlayerName}': target did not acknowledge within {10f:0.#}s."); continue; } if (TryGetHostedLocalPlayerName(out string hostedLocalPlayerName) && string.Equals(value.TargetPlayerName, hostedLocalPlayerName, StringComparison.Ordinal)) { ZRoutedRpc.instance.InvokeRoutedRPC("BossRules BossStone Reset Apply", new object[1] { value.RequestId }); value.NextRetryAt = realtimeSinceStartup + 0.5f; continue; } ZNetPeer peerByPlayerName = ZNet.instance.GetPeerByPlayerName(value.TargetPlayerName); if (peerByPlayerName == null || !peerByPlayerName.IsReady()) { value.NextRetryAt = realtimeSinceStartup + 0.5f; continue; } ZRoutedRpc.instance.InvokeRoutedRPC(peerByPlayerName.m_uid, "BossRules BossStone Reset Apply", new object[1] { value.RequestId }); value.NextRetryAt = realtimeSinceStartup + 0.5f; } } internal static bool TryInspectCurrentTarget(out string[] lines, out string error) { error = ""; Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { lines = Array.Empty(); error = "Local player is not ready."; return false; } GameObject hoverObject = ((Humanoid)localPlayer).GetHoverObject(); if ((Object)(object)hoverObject == (Object)null) { lines = Array.Empty(); error = "No hover target."; return false; } ItemStand itemStand = hoverObject.GetComponent() ?? hoverObject.GetComponentInParent(); if (!TryGetBossStone(itemStand, out BossStone bossStone) || (Object)(object)bossStone == (Object)null) { lines = Array.Empty(); error = "Current hover target is not a boss stone item stand."; return false; } Location bossStoneLocation = GetBossStoneLocation(bossStone); string text = (((Object)(object)bossStoneLocation != (Object)null) ? Utils.GetPrefabName(((Object)((Component)bossStoneLocation).gameObject).name) : ""); string playerKey = GetPlayerKey(bossStone); string[] obj = new string[6] { "BossStone: yes", $"PerPlayerEligible: {IsPerPlayerBossStone(bossStone)}", "Location: " + text, null, null, null }; ItemStand itemStand2 = bossStone.m_itemStand; object obj2; if (itemStand2 == null) { obj2 = null; } else { StatusEffect guardianPower = itemStand2.m_guardianPower; obj2 = ((guardianPower != null) ? ((Object)guardianPower).name : null); } if (obj2 == null) { obj2 = ""; } obj[3] = "GuardianPower: " + (string?)obj2; obj[4] = "PlayerKey: " + playerKey; obj[5] = $"ActiveForLocalPlayer: {LocalPlayerHasStoneActive(itemStand)}"; lines = obj; return true; } internal static bool TryHandleUseItem(ItemStand itemStand, Humanoid user, ItemData item, out bool result) { result = false; if (TryResolveBossStoneHandle(itemStand, out string _) && !((Object)(object)Player.m_localPlayer == (Object)null)) { Player val = (Player)(object)((user is Player) ? user : null); if (val != null && !((Object)(object)val != (Object)(object)Player.m_localPlayer)) { if (item == null || !CanAcceptConfiguredSacrifice(itemStand, item) || !((Humanoid)val).GetInventory().ContainsItem(item)) { ((Character)val).Message((MessageType)2, "$piece_itemstand_cantattach", 0, (Sprite)null); result = true; return true; } if (!TryGetBossStone(itemStand, out BossStone bossStone) || (Object)(object)bossStone == (Object)null) { return false; } long requestId = _nextBossStoneSacrificeRequestId++; if (!TryApplyLocalBossStoneSacrifice(val, bossStone, item, requestId)) { result = true; return true; } TryBroadcastSacrifice(bossStone, requestId); result = true; return true; } } return false; } internal static bool TryOverrideHaveAttachment(ItemStand itemStand, out bool result) { result = false; if (!TryResolveBossStoneHandle(itemStand, out string playerKey)) { return false; } result = (Object)(object)Player.m_localPlayer != (Object)null && ((Humanoid)Player.m_localPlayer).HaveUniqueKey(playerKey); return true; } internal static bool TryOverrideUpdateVisual(ItemStand itemStand) { if (!TryResolveBossStoneHandle(itemStand, out string playerKey)) { return false; } object obj; if (!((Object)(object)Player.m_localPlayer != (Object)null) || !((Humanoid)Player.m_localPlayer).HaveUniqueKey(playerKey)) { obj = ""; } else { List supportedItems = itemStand.m_supportedItems; if (supportedItems == null) { obj = null; } else { ItemDrop? obj2 = supportedItems.FirstOrDefault(); obj = ((obj2 != null) ? ((Object)obj2).name : null); } if (obj == null) { obj = ""; } } string itemName = (string)obj; int orientation = GetOrientation(itemStand); SetVisualItem(itemStand, itemName, 0, 1, orientation); return true; } internal static int ClearBossStonePlayerKeys(Player player) { if ((Object)(object)player == (Object)null) { return 0; } List list = (from key in player.GetUniqueKeys() where key?.StartsWith("dns_bossstone_", StringComparison.Ordinal) ?? false select key).ToList(); foreach (string item in list) { ((Humanoid)player).RemoveUniqueKey(item); } if (list.Count > 0 && (Object)(object)player == (Object)(object)Player.m_localPlayer) { ForsakenPowerSelectionRuntime.InvalidateGuardianPowerCache(); } return list.Count; } internal static bool LocalPlayerHasStoneActive(ItemStand? itemStand) { if (!TryResolveBossStoneHandle(itemStand, out string playerKey) || (Object)(object)Player.m_localPlayer == (Object)null) { return false; } return ((Humanoid)Player.m_localPlayer).HaveUniqueKey(playerKey); } internal static bool HasUnlockedGuardianPower(Player? player, string guardianPowerName) { if ((Object)(object)player == (Object)null || string.IsNullOrWhiteSpace(guardianPowerName)) { return false; } return ((Humanoid)player).HaveUniqueKey("dns_bossstone_" + guardianPowerName.Trim()); } internal static List GetUnlockedGuardianPowerNames(Player? player) { if ((Object)(object)player == (Object)null) { return new List(); } return (from key in player.GetUniqueKeys() where key?.StartsWith("dns_bossstone_", StringComparison.Ordinal) ?? false select key.Substring("dns_bossstone_".Length) into key where key.Length > 0 select key).Distinct(StringComparer.Ordinal).ToList(); } private static bool CanAcceptConfiguredSacrifice(ItemStand itemStand, ItemData item) { if ((Object)(object)itemStand == (Object)null || item == null) { return false; } if (!itemStand.CanAttach(item)) { return false; } List supportedItems = itemStand.m_supportedItems; if (supportedItems == null || supportedItems.Count == 0) { return true; } string text = item.m_shared?.m_name ?? ""; GameObject dropPrefab = item.m_dropPrefab; string text2 = ((dropPrefab != null) ? ((Object)dropPrefab).name : null) ?? ""; foreach (ItemDrop item2 in supportedItems) { if (!((Object)(object)item2 == (Object)null)) { string b = item2.m_itemData?.m_shared?.m_name ?? ""; string b2 = (((Object)(object)((Component)item2).gameObject != (Object)null) ? ((Object)((Component)item2).gameObject).name : ((Object)item2).name); if ((text.Length > 0 && string.Equals(text, b, StringComparison.OrdinalIgnoreCase)) || (text2.Length > 0 && string.Equals(text2, b2, StringComparison.OrdinalIgnoreCase))) { return true; } } } return false; } private static bool CanAcceptConfiguredSacrifice(ItemStand itemStand, string itemPrefabName) { if ((Object)(object)itemStand == (Object)null || string.IsNullOrWhiteSpace(itemPrefabName)) { return false; } List supportedItems = itemStand.m_supportedItems; if (supportedItems == null || supportedItems.Count == 0) { return false; } string a = itemPrefabName.Trim(); foreach (ItemDrop item in supportedItems) { if (!((Object)(object)item == (Object)null)) { string b = (((Object)(object)((Component)item).gameObject != (Object)null) ? ((Object)((Component)item).gameObject).name : ((Object)item).name); if (string.Equals(a, b, StringComparison.OrdinalIgnoreCase)) { return true; } } } return false; } private static bool TryApplyLocalBossStoneSacrifice(Player localPlayer, BossStone bossStone, ItemData item, long requestId) { if (!TryConsumeLocalBossStoneSacrifice(localPlayer, item)) { ((Character)localPlayer).Message((MessageType)2, "$piece_itemstand_cantattach", 0, (Sprite)null); return false; } string playerKey = GetPlayerKey(bossStone); if (!((Humanoid)localPlayer).HaveUniqueKey(playerKey)) { ((Humanoid)localPlayer).AddUniqueKey(playerKey); ForsakenPowerSelectionRuntime.InvalidateGuardianPowerCache(); } RefreshAllBossStoneVisuals(); return true; } private static bool TryResolveKnownPlayerName(string playerName, out string resolvedPlayerName) { //IL_0073: Unknown result type (might be due to invalid IL or missing references) resolvedPlayerName = ""; string text = (playerName ?? "").Trim(); if (text.Length == 0) { return false; } if ((Object)(object)Player.m_localPlayer != (Object)null && string.Equals(Player.m_localPlayer.GetPlayerName(), text, StringComparison.OrdinalIgnoreCase)) { resolvedPlayerName = Player.m_localPlayer.GetPlayerName(); return true; } ZNet instance = ZNet.instance; foreach (PlayerInfo item in ((instance != null) ? instance.GetPlayerList() : null) ?? new List()) { string text2 = (item.m_name ?? "").Trim(); if (string.Equals(text2, text, StringComparison.OrdinalIgnoreCase)) { resolvedPlayerName = text2; return true; } } return false; } private static bool TryGetBossStone(ItemStand? itemStand, out BossStone? bossStone) { bossStone = null; if ((Object)(object)itemStand == (Object)null || (Object)(object)itemStand.m_guardianPower == (Object)null) { return false; } bossStone = ((Component)itemStand).GetComponentInParent(); return (Object)(object)bossStone != (Object)null; } private static bool TryResolveBossStoneHandle(ItemStand? itemStand, out string playerKey) { playerKey = ""; if (!BossRulesConfig.IsPerPlayerBossStonesEnabled() || (Object)(object)itemStand == (Object)null || (Object)(object)itemStand.m_guardianPower == (Object)null) { return false; } string text = ((Object)itemStand.m_guardianPower).name?.Trim() ?? ""; BossStoneItemStandRuntimeState bossStoneItemStandRuntimeState = ((Component)itemStand).GetComponent() ?? ((Component)itemStand).gameObject.AddComponent(); if (bossStoneItemStandRuntimeState.Resolved && string.Equals(bossStoneItemStandRuntimeState.GuardianPowerName, text, StringComparison.Ordinal)) { playerKey = bossStoneItemStandRuntimeState.PlayerKey; return bossStoneItemStandRuntimeState.ShouldHandle; } bossStoneItemStandRuntimeState.Resolved = true; bossStoneItemStandRuntimeState.GuardianPowerName = text; bossStoneItemStandRuntimeState.ShouldHandle = false; bossStoneItemStandRuntimeState.PlayerKey = ""; if (!TryGetBossStone(itemStand, out BossStone bossStone) || (Object)(object)bossStone == (Object)null) { return false; } Location bossStoneLocation = GetBossStoneLocation(bossStone); if ((Object)(object)bossStoneLocation == (Object)null) { bossStoneItemStandRuntimeState.Resolved = false; return false; } if (!PerPlayerBossStoneLocationPrefabs.Contains(Utils.GetPrefabName(((Object)((Component)bossStoneLocation).gameObject).name))) { return false; } bossStoneItemStandRuntimeState.ShouldHandle = true; bossStoneItemStandRuntimeState.PlayerKey = GetPlayerKey(bossStone); playerKey = bossStoneItemStandRuntimeState.PlayerKey; return true; } private static Location? GetBossStoneLocation(BossStone? bossStone) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)bossStone == (Object)null)) { return Location.GetLocation(((Component)bossStone).transform.position, true); } return null; } private static bool IsPerPlayerBossStone(BossStone? bossStone) { Location bossStoneLocation = GetBossStoneLocation(bossStone); if ((Object)(object)bossStoneLocation != (Object)null) { return PerPlayerBossStoneLocationPrefabs.Contains(Utils.GetPrefabName(((Object)((Component)bossStoneLocation).gameObject).name)); } return false; } private static string GetPlayerKey(BossStone bossStone) { string guardianPowerName; string text = (TryResolveBossStoneGuardianPowerName(bossStone, out guardianPowerName) ? guardianPowerName : Utils.GetPrefabName(((Object)((Component)bossStone).gameObject).name)); return "dns_bossstone_" + text; } private static bool TryResolveBossStoneGuardianPowerName(BossStone? bossStone, out string guardianPowerName) { object obj; if (bossStone == null) { obj = null; } else { ItemStand itemStand = bossStone.m_itemStand; if (itemStand == null) { obj = null; } else { StatusEffect guardianPower = itemStand.m_guardianPower; obj = ((guardianPower == null) ? null : ((Object)guardianPower).name?.Trim()); } } if (obj == null) { obj = ""; } guardianPowerName = (string)obj; if (guardianPowerName.Length > 0) { return true; } string text = (((Object)(object)((bossStone != null) ? ((Component)bossStone).gameObject : null) != (Object)null) ? Utils.GetPrefabName(((Object)((Component)bossStone).gameObject).name).Trim() : ""); if (text.Length == 0) { return false; } if (text.StartsWith("GP_", StringComparison.Ordinal)) { guardianPowerName = text; return true; } if (text.StartsWith("BossStone_", StringComparison.OrdinalIgnoreCase) && text.Length > "BossStone_".Length) { guardianPowerName = "GP_" + text.Substring("BossStone_".Length); return true; } return false; } private static bool IsLocalPlayerInsideTemple(BossStone bossStone) { //IL_0026: Unknown result type (might be due to invalid IL or missing references) Player localPlayer = Player.m_localPlayer; Location bossStoneLocation = GetBossStoneLocation(bossStone); if ((Object)(object)localPlayer != (Object)null && (Object)(object)bossStoneLocation != (Object)null) { return bossStoneLocation.IsInside(((Component)localPlayer).transform.position, 0f, false); } return false; } private static bool TryBroadcastSacrifice(BossStone bossStone, long requestId) { //IL_003d: Unknown result type (might be due to invalid IL or missing references) EnsureRpcRegistered(); if (ZRoutedRpc.instance == null) { return false; } ZRoutedRpc.instance.InvokeRoutedRPC(ZRoutedRpc.Everybody, "BossRules BossStone Sacrifice Request", new object[3] { requestId, GetPlayerKey(bossStone), ((Component)bossStone).transform.position }); return true; } private static ZNetView? GetItemStandZNetView(ItemStand? itemStand) { if ((Object)(object)itemStand == (Object)null) { return null; } object obj; if (!((Object)(object)itemStand.m_netViewOverride != (Object)null)) { obj = ItemStandNviewRef.Invoke(itemStand); if (obj == null) { return ((Component)itemStand).GetComponent(); } } else { obj = itemStand.m_netViewOverride; } return (ZNetView?)obj; } private static void OnBossStoneSacrificeRequestRpc(long sender, long requestId, string playerKey, Vector3 bossStonePosition) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) string normalizedPlayerKey; bool num = TryNormalizePlayerKey(playerKey, out normalizedPlayerKey); Player localPlayer = Player.m_localPlayer; Location location = Location.GetLocation(bossStonePosition, true); bool flag = (Object)(object)localPlayer != (Object)null && (Object)(object)location != (Object)null && location.IsInside(((Component)localPlayer).transform.position, 0f, false); if (num && !((Object)(object)localPlayer == (Object)null) && !((Object)(object)location == (Object)null) && flag) { if (!((Humanoid)localPlayer).HaveUniqueKey(normalizedPlayerKey)) { ((Humanoid)localPlayer).AddUniqueKey(normalizedPlayerKey); ForsakenPowerSelectionRuntime.InvalidateGuardianPowerCache(); } RefreshAllBossStoneVisuals(); } } private static void OnBossStoneResetRequestRpc(long sender, string exactPlayerName) { if (ZRoutedRpc.instance != null && !((Object)(object)ZNet.instance == (Object)null) && ZNet.instance.IsServer()) { if (!TryResolveKnownPlayerName(exactPlayerName, out string resolvedPlayerName)) { SendBossStoneResetStatus(sender, "Boss stone reset failed: player '" + exactPlayerName?.Trim() + "' was not found."); return; } long num = _nextBossStoneResetRequestId++; PendingBossStoneResetRequests[num] = new PendingBossStoneResetRequest { RequestId = num, RequesterPeerId = sender, TargetPlayerName = resolvedPlayerName, CreatedAt = Time.realtimeSinceStartup, NextRetryAt = Time.realtimeSinceStartup }; ProcessPendingResetRequests(); } } private static void OnBossStoneResetApplyRpc(long sender, long requestId) { bool num = IsServerRoutedSender(sender); Player localPlayer = Player.m_localPlayer; if (num && !((Object)(object)localPlayer == (Object)null)) { int num2 = ClearBossStonePlayerKeys(localPlayer); RefreshAllBossStoneVisuals(); Console instance = Console.instance; if (instance != null) { instance.Print($"Removed {num2} boss stone keys from '{localPlayer.GetPlayerName()}'."); } ZRoutedRpc instance2 = ZRoutedRpc.instance; if (instance2 != null) { instance2.InvokeRoutedRPC("BossRules BossStone Reset Ack", new object[2] { requestId, num2 }); } } } private static void OnBossStoneResetAckRpc(long sender, long requestId, int removedCount) { if (ZRoutedRpc.instance != null && !((Object)(object)ZNet.instance == (Object)null) && ZNet.instance.IsServer() && PendingBossStoneResetRequests.TryGetValue(requestId, out PendingBossStoneResetRequest value) && value != null) { CompletePendingBossStoneResetRequest(requestId, $"Boss stone reset completed for '{value.TargetPlayerName}': removed {removedCount} key(s)."); } } private static void OnBossStoneResetStatusRpc(long sender, string message) { if (IsServerRoutedSender(sender)) { Console instance = Console.instance; if (instance != null) { instance.Print(message); } } } private static bool TryGetHostedLocalPlayerName(out string hostedLocalPlayerName) { hostedLocalPlayerName = ""; if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer() || (Object)(object)Player.m_localPlayer == (Object)null) { return false; } hostedLocalPlayerName = Player.m_localPlayer.GetPlayerName()?.Trim() ?? ""; return hostedLocalPlayerName.Length > 0; } private static bool TryNormalizePlayerKey(string playerKey, out string normalizedPlayerKey) { normalizedPlayerKey = (playerKey ?? "").Trim(); if (normalizedPlayerKey.StartsWith("dns_bossstone_", StringComparison.Ordinal)) { return normalizedPlayerKey.Length > "dns_bossstone_".Length; } return false; } private static bool TryConsumeLocalBossStoneSacrifice(Player localPlayer, ItemData item) { if ((Object)(object)localPlayer == (Object)null || item == null) { return false; } bool flag = false; GameObject dropPrefab = item.m_dropPrefab; string text = ((dropPrefab != null) ? ((Object)dropPrefab).name : null) ?? ""; string text2 = item.m_shared?.m_name ?? text; if (((Humanoid)localPlayer).GetInventory().ContainsItem(item)) { ((Humanoid)localPlayer).UnequipItem(item, false); flag = ((Humanoid)localPlayer).GetInventory().RemoveOneItem(item); if (flag) { ((Character)localPlayer).ShowRemovedMessage(item, 1); } } else if (text2.Length > 0 && ((Humanoid)localPlayer).GetInventory().CountItems(text2, -1, true) > 0) { ((Humanoid)localPlayer).GetInventory().RemoveItem(text2, 1, -1, true); flag = true; } return flag; } private static void CompletePendingBossStoneResetRequest(long requestId, string message) { if (PendingBossStoneResetRequests.TryGetValue(requestId, out PendingBossStoneResetRequest value) && value != null) { PendingBossStoneResetRequests.Remove(requestId); SendBossStoneResetStatus(value.RequesterPeerId, message); } } private static void SendBossStoneResetStatus(long requesterPeerId, string message) { if (ZRoutedRpc.instance == null) { Console instance = Console.instance; if (instance != null) { instance.Print(message); } } else if (requesterPeerId == 0L) { Console instance2 = Console.instance; if (instance2 != null) { instance2.Print(message); } } else { ZRoutedRpc.instance.InvokeRoutedRPC(requesterPeerId, "BossRules BossStone Reset Status", new object[1] { message }); } } private static bool IsServerRoutedSender(long sender) { if (ZRoutedRpc.instance == null || (Object)(object)ZNet.instance == (Object)null) { return false; } if (ZNet.instance.IsServer()) { return RoutedRpcIdRef.Invoke(ZRoutedRpc.instance) == sender; } ZNetPeer serverPeer = ZNet.instance.GetServerPeer(); if (serverPeer != null) { return serverPeer.m_uid == sender; } return false; } private static int GetOrientation(ItemStand itemStand) { object obj = ItemStandGetOrientationMethod?.Invoke(itemStand, null); if (obj is int) { return (int)obj; } return 0; } private static void SetVisualItem(ItemStand itemStand, string itemName, int variant, int quality, int orientation) { ItemStandSetVisualItemMethod?.Invoke(itemStand, new object[4] { itemName, variant, quality, orientation }); } private static void RefreshAllBossStoneVisuals() { if ((Object)(object)ZNetScene.instance == (Object)null) { return; } BossStone[] array = Object.FindObjectsByType((FindObjectsSortMode)0); foreach (BossStone val in array) { if ((Object)(object)val != (Object)null && IsPerPlayerBossStone(val)) { TryOverrideUpdateVisual(val.m_itemStand); } } } } [HarmonyPatch(typeof(ItemStand), "UseItem")] internal static class ItemStandUseItemBossStonePerPlayerPatch { private static bool Prefix(ItemStand __instance, Humanoid user, ItemData item, ref bool __result) { if (BossStonePerPlayerRuntime.TryHandleUseItem(__instance, user, item, out var result)) { __result = result; return false; } return true; } } [HarmonyPatch(typeof(ItemStand), "HaveAttachment")] internal static class ItemStandHaveAttachmentBossStonePerPlayerPatch { private static bool Prefix(ItemStand __instance, ref bool __result) { if (BossStonePerPlayerRuntime.TryOverrideHaveAttachment(__instance, out var result)) { __result = result; return false; } return true; } } [HarmonyPatch(typeof(ItemStand), "UpdateVisual")] internal static class ItemStandUpdateVisualBossStonePerPlayerPatch { private static bool Prefix(ItemStand __instance) { return !BossStonePerPlayerRuntime.TryOverrideUpdateVisual(__instance); } } [HarmonyPatch(typeof(Player), "Update")] internal static class PlayerUpdateForsakenPowerSelectionPatch { private static void Postfix(Player __instance) { ForsakenPowerSelectionRuntime.TryRotateSelection(__instance); } } [HarmonyPatch(typeof(Hud), "UpdateGuardianPower")] internal static class HudUpdateGuardianPowerForsakenPowerSelectionPatch { private static void Postfix(Player player) { ForsakenPowerSelectionRuntime.UpdateHudHint(Hud.instance, player); } } internal static class BossTamedPressureRuntime { private sealed class Rule { public HashSet BossPrefabs { get; } = new HashSet(StringComparer.OrdinalIgnoreCase); public HashSet ExcludedBossPrefabs { get; } = new HashSet(StringComparer.OrdinalIgnoreCase); public HashSet ExcludedTamedPrefabs { get; } = new HashSet(StringComparer.OrdinalIgnoreCase); public HashSet ExtraPressuredPrefabs { get; } = new HashSet(StringComparer.OrdinalIgnoreCase); public HashSet BossPrefabHashes { get; } = new HashSet(); public HashSet ExcludedBossPrefabHashes { get; } = new HashSet(); public HashSet ExcludedTamedPrefabHashes { get; } = new HashSet(); public HashSet ExtraPressuredPrefabHashes { get; } = new HashSet(); public float Range { get; set; } public int MaxTargetsPerBoss { get; set; } public float PercentMaxHealthPerSecond { get; set; } public float IncomingDamageMultiplier { get; set; } public float OutgoingDamageMultiplier { get; set; } public string? Message { get; set; } public double NextScanAt { get; set; } public double NextDamageAt { get; set; } public int CachedBossPrefabHashSignature { get; set; } = -1; public List CachedBossPrefabHashes { get; } = new List(); public Dictionary Targets { get; } = new Dictionary(); public Dictionary NextMessageByPlayer { get; } = new Dictionary(); } private sealed class CharacterPrefabCatalog { public static CharacterPrefabCatalog Empty { get; } = new CharacterPrefabCatalog(); public int GameDataSignature { get; set; } = -1; public HashSet CharacterPrefabHashes { get; } = new HashSet(); public HashSet MonsterAiCharacterPrefabHashes { get; } = new HashSet(); public HashSet PlayerPrefabHashes { get; } = new HashSet(); public Dictionary PrefabNamesByHash { get; } = new Dictionary(); public Dictionary BaseHealthByHash { get; } = new Dictionary(); } private readonly struct BossCandidate { internal ZDO Zdo { get; } internal Vector3 Position { get; } internal BossCandidate(ZDO zdo, Vector3 position) { //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) Zdo = zdo; Position = position; } } private readonly struct TargetCandidate { internal ZDO Zdo { get; } internal Vector3 Position { get; } internal float DistanceSqr { get; } internal int Order { get; } internal TargetCandidate(ZDO zdo, Vector3 position, float distanceSqr, int order) { //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) Zdo = zdo; Position = position; DistanceSqr = distanceSqr; Order = order; } } private sealed class TrackedTarget { public int PrefabHash { get; set; } public Vector3 LastKnownPosition { get; set; } public double ExpiresAt { get; set; } } private const float DefaultRange = 24f; private const float ScanInterval = 5f; private const float DamageInterval = 1f; private const int DefaultMaxTargetsPerBoss = 6; private const float DefaultPercentMaxHealthPerSecond = 0.007f; private const float DefaultIncomingDamageMultiplier = 1f; private const float DefaultOutgoingDamageMultiplier = 1f; private const float MessageInterval = 5f; private const string DefaultMessage = "Tamed creatures near a boss are weakened."; private static readonly int ActiveUntilKey = StringExtensionMethods.GetStableHashCode("BossRules_BossTamedPressure_Until"); private static readonly int IncomingMultiplierKey = StringExtensionMethods.GetStableHashCode("BossRules_BossTamedPressure_Incoming"); private static readonly int OutgoingMultiplierKey = StringExtensionMethods.GetStableHashCode("BossRules_BossTamedPressure_Outgoing"); private static readonly int GenerationKey = StringExtensionMethods.GetStableHashCode("BossRules_BossTamedPressure_Generation"); private static readonly List Rules = new List(); private static readonly List BossCandidateBuffer = new List(); private static readonly List TargetCandidateBuffer = new List(); private static readonly List BossZdoBuffer = new List(); private static readonly List SectorZdoBuffer = new List(); private static readonly List TargetIdBuffer = new List(); private static int CurrentGeneration = 1; private static CharacterPrefabCatalog _characterPrefabCatalog = CharacterPrefabCatalog.Empty; internal static void Configure(IEnumerable definitions, string? message) { AdvanceGeneration(); Rules.Clear(); ClearTransientBuffers(); foreach (BossTamedPressureDefinition item in definitions ?? Enumerable.Empty()) { Rules.Add(CompileRule(item, message)); } } internal static void ExecuteServerTick() { double timeSeconds = GetTimeSeconds(); if ((Object)(object)ZNet.instance == (Object)null || !BossRulesPlugin.IsRuntimeServer() || !BossRulesConfig.IsBossTamedPressureEnabled() || Rules.Count == 0) { return; } foreach (Rule rule in Rules) { if (timeSeconds >= rule.NextScanAt) { ScanRule(rule, timeSeconds); rule.NextScanAt = timeSeconds + 5.0; } if (timeSeconds >= rule.NextDamageAt) { ApplyPeriodicDamage(rule, timeSeconds); rule.NextDamageAt = timeSeconds + 1.0; } } } internal static void ApplyDamageMultipliers(Character? victim, HitData? hit) { if (!((Object)(object)victim == (Object)null) && hit != null && hit.HaveAttacker() && BossRulesConfig.IsBossTamedPressureEnabled()) { double timeSeconds = GetTimeSeconds(); float num = 1f; float multiplier = 1f; if (TryGetCharacterZdo(victim, out ZDO zdo) && zdo != null && TryGetActiveMultiplier(zdo, IncomingMultiplierKey, timeSeconds, out multiplier)) { num *= multiplier; } ZDO val = ResolveAttackerZdo(hit); float multiplier2 = 1f; if (val != null && TryGetActiveMultiplier(val, OutgoingMultiplierKey, timeSeconds, out multiplier2)) { num *= multiplier2; } if (!Mathf.Approximately(num, 1f)) { float num2 = Mathf.Max(0f, num); hit.ApplyModifier(num2); } } } private static Rule CompileRule(BossTamedPressureDefinition definition, string? message) { BossTamedPressureTargetsDefinition targets = definition.Targets; BossTamedPressurePressureDefinition pressure = definition.Pressure; Rule rule = new Rule { Range = Mathf.Clamp(targets?.Range ?? 24f, 0f, 128f), MaxTargetsPerBoss = Mathf.Clamp(targets?.MaxPerBoss ?? 6, 1, 128), PercentMaxHealthPerSecond = Mathf.Clamp01(pressure?.DamagePercentPerSecond ?? 0.007f), IncomingDamageMultiplier = Mathf.Clamp(pressure?.IncomingDamageMultiplier ?? 1f, 0f, 10f), OutgoingDamageMultiplier = Mathf.Clamp(pressure?.OutgoingDamageMultiplier ?? 1f, 0f, 10f), Message = (message ?? "Tamed creatures near a boss are weakened.") }; AddAll(rule.BossPrefabs, rule.BossPrefabHashes, definition.BossPrefabs); AddAll(rule.ExcludedBossPrefabs, rule.ExcludedBossPrefabHashes, definition.ExcludedBossPrefabs); AddAll(rule.ExcludedTamedPrefabs, rule.ExcludedTamedPrefabHashes, targets?.ExcludedTamedPrefabs); AddAll(rule.ExtraPressuredPrefabs, rule.ExtraPressuredPrefabHashes, targets?.ExtraPressuredPrefabs); return rule; } private static void ScanRule(Rule rule, double now) { //IL_00b9: 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_00db: Unknown result type (might be due to invalid IL or missing references) CharacterPrefabCatalog catalog = EnsureCharacterPrefabCatalog(); List bossCandidateBuffer = BossCandidateBuffer; bossCandidateBuffer.Clear(); BuildBossCandidates(rule, bossCandidateBuffer, catalog, BossZdoBuffer); if (bossCandidateBuffer.Count == 0) { return; } float rangeSqr = rule.Range * rule.Range; List targetCandidateBuffer = TargetCandidateBuffer; foreach (BossCandidate item in bossCandidateBuffer) { CollectTargetsNearBoss(rule, catalog, item, rangeSqr, targetCandidateBuffer, SectorZdoBuffer); if (targetCandidateBuffer.Count == 0) { continue; } if (targetCandidateBuffer.Count > 1) { targetCandidateBuffer.Sort(delegate(TargetCandidate left, TargetCandidate right) { int num2 = left.DistanceSqr.CompareTo(right.DistanceSqr); return (num2 == 0) ? left.Order.CompareTo(right.Order) : num2; }); } int num = 0; foreach (TargetCandidate item2 in targetCandidateBuffer) { if (!(item2.Zdo.m_uid == item.Zdo.m_uid)) { if (TrackTarget(rule, item2.Zdo, item2.Position, now)) { num++; } if (num >= rule.MaxTargetsPerBoss) { break; } } } } bossCandidateBuffer.Clear(); targetCandidateBuffer.Clear(); } private static void BuildBossCandidates(Rule rule, List bosses, CharacterPrefabCatalog catalog, List bossZdos) { //IL_007f: Unknown result type (might be due to invalid IL or missing references) bossZdos.Clear(); if (ZDOMan.instance == null) { return; } foreach (int bossPrefabHash in GetBossPrefabHashes(rule, catalog)) { if (!catalog.PrefabNamesByHash.TryGetValue(bossPrefabHash, out string value) || string.IsNullOrWhiteSpace(value)) { continue; } bossZdos.Clear(); int num = 0; while (!ZDOMan.instance.GetAllZDOsWithPrefabIterative(value, bossZdos, ref num)) { } foreach (ZDO bossZdo in bossZdos) { if (IsValidLiveZdo(bossZdo, GetBaseHealth(catalog, bossPrefabHash))) { bosses.Add(new BossCandidate(bossZdo, bossZdo.GetPosition())); } } } bossZdos.Clear(); } private static int CollectTargetsNearBoss(Rule rule, CharacterPrefabCatalog catalog, BossCandidate boss, float rangeSqr, List nearbyTargets, List sectorObjects) { //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) //IL_0071: 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_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_009d: 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_00b3: Unknown result type (might be due to invalid IL or missing references) nearbyTargets.Clear(); sectorObjects.Clear(); if (ZDOMan.instance == null || (Object)(object)ZoneSystem.instance == (Object)null) { return 0; } int num = Mathf.Max(0, Mathf.CeilToInt(rule.Range / 64f) + 1); ZDOMan.instance.FindSectorObjects(ZoneSystem.GetZone(boss.Position), num, 0, sectorObjects, (List)null); int num2 = 0; foreach (ZDO sectorObject in sectorObjects) { if (sectorObject != null && !(sectorObject.m_uid == boss.Zdo.m_uid) && IsEligiblePressureTarget(rule, sectorObject, catalog)) { Vector3 position = sectorObject.GetPosition(); float horizontalDistanceSqr = GetHorizontalDistanceSqr(boss.Position, position); if (!(horizontalDistanceSqr > rangeSqr)) { nearbyTargets.Add(new TargetCandidate(sectorObject, position, horizontalDistanceSqr, num2++)); } } } int count = sectorObjects.Count; sectorObjects.Clear(); return count; } private static bool TrackTarget(Rule rule, ZDO zdo, Vector3 position, double now) { //IL_000e: 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_0037: 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_012a: Unknown result type (might be due to invalid IL or missing references) if (zdo == null || !zdo.IsValid()) { return false; } ZDOID uid = zdo.m_uid; int prefab = zdo.GetPrefab(); double num = now + 5.0 + 0.5; rule.Targets[uid] = new TrackedTarget { PrefabHash = prefab, LastKnownPosition = position, ExpiresAt = num }; float num2 = zdo.GetFloat(ActiveUntilKey, 0f); float num3 = (float)Math.Max(num2, num); zdo.Set(ActiveUntilKey, num3); float num4 = rule.IncomingDamageMultiplier; float num5 = rule.OutgoingDamageMultiplier; if ((double)num2 > now && zdo.GetInt(GenerationKey, 0) == CurrentGeneration) { num4 = Math.Max(zdo.GetFloat(IncomingMultiplierKey, 1f), num4); num5 = Math.Min(zdo.GetFloat(OutgoingMultiplierKey, 1f), num5); } zdo.Set(GenerationKey, CurrentGeneration, false); zdo.Set(IncomingMultiplierKey, Mathf.Clamp(num4, 0f, 10f)); zdo.Set(OutgoingMultiplierKey, Mathf.Clamp(num5, 0f, 10f)); ZDOMan instance = ZDOMan.instance; if (instance != null) { instance.ForceSendZDO(uid); } return true; } private static void ApplyPeriodicDamage(Rule rule, double now) { //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_0044: 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_0075: 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_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_00de: Unknown result type (might be due to invalid IL or missing references) //IL_00e3: 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_00ea: 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_00f8: Expected O, but got Unknown //IL_011a: Unknown result type (might be due to invalid IL or missing references) //IL_0136: Unknown result type (might be due to invalid IL or missing references) if (rule.PercentMaxHealthPerSecond <= 0f) { RemoveExpiredTargets(rule, now); return; } CharacterPrefabCatalog catalog = EnsureCharacterPrefabCatalog(); CopyTargetIds(rule, TargetIdBuffer); foreach (ZDOID item in TargetIdBuffer) { if (!rule.Targets.TryGetValue(item, out TrackedTarget value) || value.ExpiresAt < now) { rule.Targets.Remove(item); continue; } ZDOMan instance = ZDOMan.instance; ZDO val = ((instance != null) ? instance.GetZDO(item) : null); if (val == null || !IsEligiblePressureTarget(rule, val, catalog)) { rule.Targets.Remove(item); continue; } value.PrefabHash = val.GetPrefab(); value.LastKnownPosition = val.GetPosition(); float num = GetMaxHealth(val, value.PrefabHash, catalog) * rule.PercentMaxHealthPerSecond * 1f; if (!(num <= 0f)) { HitData val2 = new HitData { m_hitType = (HitType)0, m_point = value.LastKnownPosition }; val2.m_damage.m_damage = num; ZRoutedRpc instance2 = ZRoutedRpc.instance; if (instance2 != null) { instance2.InvokeRoutedRPC(val.GetOwner(), val.m_uid, "RPC_Damage", new object[1] { val2 }); } TrySendMessage(rule, value.LastKnownPosition, now); } } TargetIdBuffer.Clear(); } private static void RemoveExpiredTargets(Rule rule, double now) { //IL_001a: 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_0026: 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) CopyTargetIds(rule, TargetIdBuffer); foreach (ZDOID item in TargetIdBuffer) { if (!rule.Targets.TryGetValue(item, out TrackedTarget value) || value.ExpiresAt < now) { rule.Targets.Remove(item); } } TargetIdBuffer.Clear(); } private static bool IsEligiblePressureTarget(Rule rule, ZDO zdo, CharacterPrefabCatalog catalog) { if (zdo == null || !zdo.IsValid()) { return false; } int prefab = zdo.GetPrefab(); if (prefab == 0 || !catalog.CharacterPrefabHashes.Contains(prefab) || catalog.PlayerPrefabHashes.Contains(prefab) || !IsValidLiveZdo(zdo, GetBaseHealth(catalog, prefab))) { return false; } if (rule.ExtraPressuredPrefabHashes.Count <= 0 && rule.ExcludedTamedPrefabHashes.Count <= 0) { return IsTamedMonsterAiZdo(zdo, prefab, catalog); } if (rule.ExtraPressuredPrefabHashes.Contains(prefab)) { return true; } if (IsTamedMonsterAiZdo(zdo, prefab, catalog)) { return !rule.ExcludedTamedPrefabHashes.Contains(prefab); } return false; } private static bool IsTamedMonsterAiZdo(ZDO zdo, int prefabHash, CharacterPrefabCatalog catalog) { if (zdo.GetBool(ZDOVars.s_tamed, false)) { return catalog.MonsterAiCharacterPrefabHashes.Contains(prefabHash); } return false; } private static bool IsValidLiveZdo(ZDO? zdo, float baseHealth) { if (zdo == null || !zdo.IsValid()) { return false; } float num = zdo.GetFloat(ZDOVars.s_maxHealth, Mathf.Max(baseHealth, 1f)); return zdo.GetFloat(ZDOVars.s_health, num) > 0f; } private static float GetHorizontalDistanceSqr(Vector3 origin, Vector3 target) { //IL_0000: 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_000d: 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) float num = target.x - origin.x; float num2 = target.z - origin.z; return num * num + num2 * num2; } private static CharacterPrefabCatalog EnsureCharacterPrefabCatalog() { int gameDataSignature = BossRulesRuntime.GetGameDataSignature(); if (_characterPrefabCatalog.GameDataSignature == gameDataSignature) { return _characterPrefabCatalog; } CharacterPrefabCatalog characterPrefabCatalog = new CharacterPrefabCatalog { GameDataSignature = gameDataSignature }; Character val = default(Character); foreach (GameObject item in BossRulesRuntime.EnumeratePrefabsForRuntime()) { if ((Object)(object)item == (Object)null || !item.TryGetComponent(ref val)) { continue; } string prefabNameForRuntime = BossRulesRuntime.GetPrefabNameForRuntime(item); if (!string.IsNullOrWhiteSpace(prefabNameForRuntime)) { int stableHashCode = StringExtensionMethods.GetStableHashCode(prefabNameForRuntime); characterPrefabCatalog.CharacterPrefabHashes.Add(stableHashCode); characterPrefabCatalog.PrefabNamesByHash[stableHashCode] = prefabNameForRuntime; characterPrefabCatalog.BaseHealthByHash[stableHashCode] = Mathf.Max(val.m_health, 1f); if ((Object)(object)item.GetComponent() != (Object)null) { characterPrefabCatalog.MonsterAiCharacterPrefabHashes.Add(stableHashCode); } if (val.IsPlayer()) { characterPrefabCatalog.PlayerPrefabHashes.Add(stableHashCode); } } } foreach (string autoDetectedBossPrefabName in BossRulesRuntime.GetAutoDetectedBossPrefabNames()) { string text = (autoDetectedBossPrefabName ?? "").Trim(); if (text.Length != 0) { int stableHashCode2 = StringExtensionMethods.GetStableHashCode(text); characterPrefabCatalog.PrefabNamesByHash[stableHashCode2] = text; } } _characterPrefabCatalog = characterPrefabCatalog; return _characterPrefabCatalog; } private static IReadOnlyList GetBossPrefabHashes(Rule rule, CharacterPrefabCatalog catalog) { if (rule.CachedBossPrefabHashSignature == catalog.GameDataSignature) { return rule.CachedBossPrefabHashes; } rule.CachedBossPrefabHashes.Clear(); HashSet hashSet = new HashSet(); foreach (string autoDetectedBossPrefabName in BossRulesRuntime.GetAutoDetectedBossPrefabNames()) { string text = (autoDetectedBossPrefabName ?? "").Trim(); if (text.Length != 0) { int stableHashCode = StringExtensionMethods.GetStableHashCode(text); if (stableHashCode != 0 && !rule.ExcludedBossPrefabHashes.Contains(stableHashCode) && hashSet.Add(stableHashCode)) { rule.CachedBossPrefabHashes.Add(stableHashCode); } } } foreach (int bossPrefabHash in rule.BossPrefabHashes) { if (bossPrefabHash != 0 && !rule.ExcludedBossPrefabHashes.Contains(bossPrefabHash) && hashSet.Add(bossPrefabHash)) { rule.CachedBossPrefabHashes.Add(bossPrefabHash); } } rule.CachedBossPrefabHashSignature = catalog.GameDataSignature; return rule.CachedBossPrefabHashes; } private static float GetBaseHealth(CharacterPrefabCatalog catalog, int prefabHash) { if (!catalog.BaseHealthByHash.TryGetValue(prefabHash, out var value)) { return 1f; } return Mathf.Max(value, 1f); } private static float GetMaxHealth(ZDO zdo, int prefabHash, CharacterPrefabCatalog catalog) { float baseHealth = GetBaseHealth(catalog, prefabHash); int num = Mathf.Max(1, zdo.GetInt(ZDOVars.s_level, 1)); return zdo.GetFloat(ZDOVars.s_maxHealth, baseHealth * (float)num); } private static bool TryGetCharacterZdo(Character character, out ZDO? zdo) { object obj; if (character == null) { obj = null; } else { ZNetView nview = character.m_nview; obj = ((nview != null) ? nview.GetZDO() : null); } zdo = (ZDO?)obj; return zdo != null; } private static ZDO? ResolveAttackerZdo(HitData hit) { //IL_0037: Unknown result type (might be due to invalid IL or missing references) Character attacker = hit.GetAttacker(); if ((Object)(object)attacker != (Object)null && TryGetCharacterZdo(attacker, out ZDO zdo)) { return zdo; } if (((ZDOID)(ref hit.m_attacker)).IsNone()) { return null; } ZDOMan instance = ZDOMan.instance; if (instance == null) { return null; } return instance.GetZDO(hit.m_attacker); } private static bool TryGetActiveMultiplier(ZDO zdo, int multiplierKey, double now, out float multiplier) { multiplier = 1f; if ((double)zdo.GetFloat(ActiveUntilKey, 0f) <= now) { return false; } multiplier = Mathf.Clamp(zdo.GetFloat(multiplierKey, 1f), 0f, 10f); return !Mathf.Approximately(multiplier, 1f); } private static void TrySendMessage(Rule rule, Vector3 targetPosition, double now) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) if (string.IsNullOrWhiteSpace(rule.Message) || !SceneProximityQueries.TryFindNearestLivingPlayerInRangeXZ(targetPosition, Mathf.Max(rule.Range, 32f), out var playerId) || playerId == 0L || (rule.NextMessageByPlayer.TryGetValue(playerId, out var value) && now < value)) { return; } rule.NextMessageByPlayer[playerId] = now + 5.0; if (playerId == ZNet.GetUID() && (Object)(object)Player.m_localPlayer != (Object)null) { ((Character)Player.m_localPlayer).Message((MessageType)1, rule.Message, 0, (Sprite)null); return; } ZRoutedRpc instance = ZRoutedRpc.instance; if (instance != null) { instance.InvokeRoutedRPC(playerId, "ShowMessage", new object[2] { 1, rule.Message }); } } private static double GetTimeSeconds() { ZNet instance = ZNet.instance; if (instance == null) { return Time.time; } return instance.GetTimeSeconds(); } private static void AdvanceGeneration() { CurrentGeneration++; if (CurrentGeneration <= 0) { CurrentGeneration = 1; } } private static void CopyTargetIds(Rule rule, List targetIds) { //IL_001b: 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_0022: Unknown result type (might be due to invalid IL or missing references) targetIds.Clear(); foreach (ZDOID key in rule.Targets.Keys) { targetIds.Add(key); } } private static void ClearTransientBuffers() { BossCandidateBuffer.Clear(); TargetCandidateBuffer.Clear(); BossZdoBuffer.Clear(); SectorZdoBuffer.Clear(); TargetIdBuffer.Clear(); } private static void AddAll(HashSet target, HashSet hashes, IEnumerable? values) { if (values == null) { return; } foreach (string value in values) { string text = (value ?? "").Trim(); if (text.Length > 0) { target.Add(text); hashes.Add(StringExtensionMethods.GetStableHashCode(text)); } } } } internal static class CreatureSpawnerDuplicateBlockRuntime { private static readonly int DuplicateBlockTicksZdoKey = StringExtensionMethods.GetStableHashCode("BossRules.creature_spawner_same_boss_duplicate_block_ticks"); private static readonly Dictionary LastBlockTicksBySpawnerId = new Dictionary(); internal static bool ShouldBlockUpdate(CreatureSpawner? creatureSpawner) { //IL_0042: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)creatureSpawner == (Object)null || !TryGetCreatureSpawnerTimingZdo(creatureSpawner, out ZNetView nview, out ZDO zdo) || !nview.IsOwner()) { return false; } float sameBossDuplicateBlockRadius = BossRulesConfig.GetSameBossDuplicateBlockRadius(); if (sameBossDuplicateBlockRadius <= 0f) { ClearBlockTicks(creatureSpawner, zdo); return false; } if (BossRulesManager.ShouldBlockSameBossSpawn(creatureSpawner.m_creaturePrefab, ((Component)creatureSpawner).transform.position, sameBossDuplicateBlockRadius)) { RecordBlock(creatureSpawner, zdo); return true; } return ShouldDelayAfterBlock(creatureSpawner, zdo); } internal static void RemoveSpawner(CreatureSpawner? creatureSpawner) { if ((Object)(object)creatureSpawner != (Object)null) { LastBlockTicksBySpawnerId.Remove(((Object)creatureSpawner).GetInstanceID()); } } internal static void ClearRuntimeState() { LastBlockTicksBySpawnerId.Clear(); } private static void RecordBlock(CreatureSpawner creatureSpawner, ZDO zdo) { long networkTimeTicks = GetNetworkTimeTicks(); LastBlockTicksBySpawnerId[((Object)creatureSpawner).GetInstanceID()] = networkTimeTicks; zdo.Set(DuplicateBlockTicksZdoKey, networkTimeTicks); zdo.Set(ZDOVars.s_aliveTime, networkTimeTicks); } private static bool ShouldDelayAfterBlock(CreatureSpawner creatureSpawner, ZDO zdo) { if (creatureSpawner.m_respawnTimeMinuts <= 0f) { ClearBlockTicks(creatureSpawner, zdo); return false; } long blockTicks = GetBlockTicks(creatureSpawner, zdo); if (blockTicks <= 0) { return false; } long networkTimeTicks = GetNetworkTimeTicks(); if (blockTicks > networkTimeTicks) { RecordBlock(creatureSpawner, zdo); return true; } if ((new DateTime(networkTimeTicks) - new DateTime(blockTicks)).TotalMinutes < (double)creatureSpawner.m_respawnTimeMinuts) { return true; } ClearBlockTicks(creatureSpawner, zdo); return false; } private static long GetBlockTicks(CreatureSpawner creatureSpawner, ZDO zdo) { LastBlockTicksBySpawnerId.TryGetValue(((Object)creatureSpawner).GetInstanceID(), out var value); return Math.Max(value, zdo.GetLong(DuplicateBlockTicksZdoKey, 0L)); } private static void ClearBlockTicks(CreatureSpawner creatureSpawner, ZDO zdo) { LastBlockTicksBySpawnerId.Remove(((Object)creatureSpawner).GetInstanceID()); zdo.Set(DuplicateBlockTicksZdoKey, 0L); } private static bool TryGetCreatureSpawnerTimingZdo(CreatureSpawner creatureSpawner, out ZNetView nview, out ZDO zdo) { nview = null; zdo = null; ZNetView val = default(ZNetView); if (!((Component)creatureSpawner).TryGetComponent(ref val) || (Object)(object)val == (Object)null) { return false; } nview = val; zdo = nview.GetZDO(); return zdo != null; } private static long GetNetworkTimeTicks() { if (!((Object)(object)ZNet.instance != (Object)null)) { return DateTime.UtcNow.Ticks; } return ZNet.instance.GetTime().Ticks; } } internal static class DespawnRefundExecutor { internal static bool TryExecuteRefunds(Vector3 centerPoint, IReadOnlyCollection refunds) { //IL_0010: 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_0097: Unknown result type (might be due to invalid IL or missing references) //IL_0098: 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_00aa: Unknown result type (might be due to invalid IL or missing references) if (refunds == null || refunds.Count == 0) { BossRulesDebugLog.Client("Despawn refund skipped at " + BossRulesDebugLog.FormatVector3(centerPoint) + ": no refunds."); return true; } BossRulesDebugLog.Client("Despawn refund executing fallback=" + BossRulesDebugLog.FormatVector3(centerPoint) + ": " + BossRulesDebugLog.FormatRefunds(refunds) + "."); bool result = false; foreach (DespawnRefundDrop refund in refunds) { if (refund != null && !((Object)(object)refund.Prefab == (Object)null) && refund.Amount > 0) { Vector3 centerPoint2 = refund.DropPointOverride ?? centerPoint; SpawnStackedDrops(refund.Prefab, refund.Amount, centerPoint2); result = true; } } return result; } private static void SpawnStackedDrops(GameObject itemPrefab, int amount, Vector3 centerPoint) { //IL_0021: 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_0027: 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_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_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) int num = Math.Max(1, amount); int maxStackSize = GetMaxStackSize(itemPrefab); ItemDrop val = default(ItemDrop); while (num > 0) { int num2 = Math.Min(num, maxStackSize); num -= num2; GameObject obj = Object.Instantiate(itemPrefab, GetDropPosition(centerPoint), Random.rotation); if (obj.TryGetComponent(ref val)) { val.SetStack(num2); val.m_itemData.m_worldLevel = (byte)Mathf.Clamp(Game.m_worldLevel, 0, 255); } Rigidbody component = obj.GetComponent(); if ((Object)(object)component != (Object)null) { component.linearVelocity = Vector3.up * Random.Range(4f, 6f) + Random.insideUnitSphere * 2f; } } } private static int GetMaxStackSize(GameObject itemPrefab) { ItemDrop val = default(ItemDrop); if (itemPrefab.TryGetComponent(ref val)) { return Mathf.Max(1, val.m_itemData.m_shared.m_maxStackSize); } return 1; } private static Vector3 GetDropPosition(Vector3 centerPoint) { //IL_0000: 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_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_001c: 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_0027: Unknown result type (might be due to invalid IL or missing references) Vector2 val = Random.insideUnitCircle * 1.5f; return centerPoint + new Vector3(val.x, 1f, val.y); } } internal sealed class DespawnRefundDrop { internal GameObject Prefab { get; } internal int Amount { get; } internal Vector3? DropPointOverride { get; } internal DespawnRefundDrop(GameObject prefab, int amount, Vector3? dropPointOverride = null) { Prefab = prefab; Amount = amount; DropPointOverride = dropPointOverride; } } internal static class DespawnRulesManager { private readonly struct PendingDespawnDetachPersist { internal ZDOID ZdoId { get; } internal Vector3 ProbePoint { get; } internal int PrefabHashHint { get; } internal string PrefabNameHint { get; } internal PendingDespawnDetachPersist(ZDOID zdoId, Vector3 probePoint, int prefabHashHint, string prefabNameHint) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: 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) ZdoId = zdoId; ProbePoint = probePoint; PrefabHashHint = prefabHashHint; PrefabNameHint = prefabNameHint ?? ""; } } private sealed class TrackedDespawnState { internal readonly List Refunds = new List(); internal string DisplayName { get; set; } = "Target"; internal string PrefabName { get; set; } = ""; internal float? RangeOverride { get; set; } internal float? DelayOverride { get; set; } internal double NoPlayerSince { get; set; } = -1.0; internal int LastAnnouncedRemainingSeconds { get; set; } = -1; internal long LastInterestedPlayerId { get; set; } internal long CountdownRecipientPlayerId { get; set; } internal long ScheduledCheckAtBucket { get; set; } = long.MinValue; internal void UpdateFromCharacter(Character character, float? rangeOverride, float? delayOverride, IReadOnlyCollection refunds) { DisplayName = GetDisplayName(character); PrefabName = Utils.GetPrefabName(((Component)character).gameObject); RangeOverride = rangeOverride; DelayOverride = delayOverride; Refunds.Clear(); if (refunds != null) { Refunds.AddRange(refunds); } } internal void UpdateFromZdoPrefab(string prefabName, float? rangeOverride, float? delayOverride, IReadOnlyCollection refunds) { DisplayName = (string.IsNullOrWhiteSpace(prefabName) ? "Target" : prefabName); PrefabName = prefabName ?? ""; RangeOverride = rangeOverride; DelayOverride = delayOverride; Refunds.Clear(); if (refunds != null) { Refunds.AddRange(refunds); } } internal void ResetCountdown() { NoPlayerSince = -1.0; LastAnnouncedRemainingSeconds = -1; CountdownRecipientPlayerId = 0L; } internal float GetEffectiveRange() { return Mathf.Clamp(RangeOverride ?? _defaultDespawnRange, 0f, 128f); } internal float GetEffectiveDelaySeconds() { return Mathf.Clamp(DelayOverride ?? _defaultDespawnDelaySeconds, 0f, 300f); } } private enum DespawnObservationSource { BootstrapScan, CreatedZdo, LoadedCharacter } private enum CreatedZdoObservationDecision { Eligible, Ineligible, Unknown } private enum ObservationApplicationResult { Applied, Dropped, Deferred } private readonly struct PendingDespawnObservation { internal ZDOID ZdoId { get; } internal int PrefabHashHint { get; } internal string PrefabNameHint { get; } internal DespawnObservationSource Source { get; } internal float NextAttemptAt { get; } internal float ExpireAt { get; } internal int RetryCount { get; } internal PendingDespawnObservation(ZDOID zdoId, int prefabHashHint, string prefabNameHint, DespawnObservationSource source, float nextAttemptAt = 0f, float expireAt = 0f, int retryCount = 0) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) ZdoId = zdoId; PrefabHashHint = prefabHashHint; PrefabNameHint = prefabNameHint ?? ""; Source = source; NextAttemptAt = nextAttemptAt; ExpireAt = expireAt; RetryCount = retryCount; } } private const float DespawnCountdownCheckIntervalSeconds = 0.5f; private const float DespawnIdleCheckIntervalSeconds = 1f; private const float DespawnTrackingRefreshIntervalSeconds = 1f; private const int DespawnFinalCountdownSeconds = 5; private const int DespawnReminderIntervalSeconds = 5; private static readonly Dictionary TrackedDespawnTargets = new Dictionary(); private static readonly SortedDictionary> ScheduledDespawnChecks = new SortedDictionary>(); private static readonly Dictionary PendingDespawnDetachPersists = new Dictionary(); private static readonly List PendingDespawnRemovals = new List(); private static readonly List PendingDespawnDetachPersistRemovals = new List(); private static readonly Stopwatch DespawnClock = Stopwatch.StartNew(); private static float _nextDespawnTrackingRefreshAt; private static float _defaultDespawnRange = 64f; private static float _defaultDespawnDelaySeconds = 90f; private const string DefaultMessageDespawnStart = "{name} will despawn in {seconds}s unless someone returns."; private const string DefaultMessageDespawnReminder = "{name} will despawn in {seconds}s."; private const string DefaultMessageDespawnCanceled = "{name} despawn canceled."; private static string _messageDespawnStart = "{name} will despawn in {seconds}s unless someone returns."; private static string _messageDespawnReminder = "{name} will despawn in {seconds}s."; private static string _messageDespawnCanceled = "{name} despawn canceled."; private const float CreatedZdoObservationRetryIntervalSeconds = 0.25f; private const float CreatedZdoObservationRetryTimeoutSeconds = 1f; private static readonly Dictionary PendingDespawnObservations = new Dictionary(); private static readonly List PendingDespawnObservationRemovals = new List(); private static readonly List PendingDespawnObservationUpdates = new List(); private static readonly List BootstrapScanBuffer = new List(); private static bool _pendingBootstrapScan = true; private static int _lastObservedDespawnLookupVersion = -1; internal static void ConfigureDefaults(float despawnRange, float despawnDelaySeconds) { _defaultDespawnRange = Mathf.Clamp(despawnRange, 0f, 128f); _defaultDespawnDelaySeconds = Mathf.Clamp(despawnDelaySeconds, 0f, 300f); } internal static void ExecuteServerTick() { if (!BossRulesPlugin.IsRuntimeServer()) { return; } if (!BossRulesConfig.IsDespawnRulesEnabled()) { if (TrackedDespawnTargets.Count > 0) { TrackedDespawnTargets.Clear(); PendingDespawnRemovals.Clear(); } if (ScheduledDespawnChecks.Count > 0) { ScheduledDespawnChecks.Clear(); } if (PendingDespawnObservations.Count > 0) { PendingDespawnObservations.Clear(); PendingDespawnObservationRemovals.Clear(); } if (PendingDespawnObservationUpdates.Count > 0) { PendingDespawnObservationUpdates.Clear(); } if (PendingDespawnDetachPersists.Count > 0) { PendingDespawnDetachPersists.Clear(); PendingDespawnDetachPersistRemovals.Clear(); } _nextDespawnTrackingRefreshAt = 0f; _pendingBootstrapScan = true; _lastObservedDespawnLookupVersion = -1; return; } ObserveDespawnLookupVersion(); ApplyPendingDespawnObservations(); float time = Time.time; if (_nextDespawnTrackingRefreshAt <= time) { PruneTrackedDespawnTargetsAgainstCurrentConfig(); if (_pendingBootstrapScan && RunPendingBootstrapScan()) { _pendingBootstrapScan = false; ApplyPendingDespawnObservations(); } _nextDespawnTrackingRefreshAt = time + 1f; } double currentDespawnClockSeconds = GetCurrentDespawnClockSeconds(); ApplyPendingDespawnDetaches(currentDespawnClockSeconds); if (TrackedDespawnTargets.Count == 0) { if (ScheduledDespawnChecks.Count > 0) { ScheduledDespawnChecks.Clear(); } } else if (ScheduledDespawnChecks.Count != 0) { ProcessScheduledDespawnChecks(currentDespawnClockSeconds); } } private static void ProcessScheduledDespawnChecks(double nowSeconds) { //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_0044: 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_0090: Unknown result type (might be due to invalid IL or missing references) //IL_0082: 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) if (ScheduledDespawnChecks.Count == 0) { return; } long nowBucket = QuantizeScheduledCheck(nowSeconds); PendingDespawnRemovals.Clear(); long bucket; List dueTargets; while (TryDequeueDueScheduledCheck(nowBucket, out bucket, out dueTargets)) { foreach (ZDOID item in dueTargets) { if (TrackedDespawnTargets.TryGetValue(item, out TrackedDespawnState value) && value.ScheduledCheckAtBucket == bucket) { value.ScheduledCheckAtBucket = long.MinValue; ZDOMan instance = ZDOMan.instance; ZDO val = ((instance != null) ? instance.GetZDO(item) : null); if (val == null || IsDeadZdo(item)) { PendingDespawnRemovals.Add(item); } else { ProcessTrackedDespawnTarget(item, val, value, nowSeconds); } } } } FlushPendingDespawnRemovals(); } private static bool TryDequeueDueScheduledCheck(long nowBucket, out long bucket, out List dueTargets) { if (ScheduledDespawnChecks.Count == 0) { bucket = long.MinValue; dueTargets = null; return false; } using IEnumerator>> enumerator = (object)ScheduledDespawnChecks.GetEnumerator(); if (!enumerator.MoveNext()) { bucket = long.MinValue; dueTargets = null; return false; } KeyValuePair> current = enumerator.Current; if (current.Key > nowBucket) { bucket = long.MinValue; dueTargets = null; return false; } bucket = current.Key; dueTargets = current.Value; ScheduledDespawnChecks.Remove(current.Key); return true; } private static void ApplyPendingDespawnDetaches(double nowSeconds) { //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_007c: 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_008f: 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_00ce: Unknown result type (might be due to invalid IL or missing references) //IL_00d5: Unknown result type (might be due to invalid IL or missing references) if (PendingDespawnDetachPersists.Count == 0 || (Object)(object)ZNetScene.instance == (Object)null || (Object)(object)ObjectDB.instance == (Object)null || ZDOMan.instance == null) { return; } PendingDespawnDetachPersistRemovals.Clear(); foreach (PendingDespawnDetachPersist value in PendingDespawnDetachPersists.Values) { ZDO zDO = ZDOMan.instance.GetZDO(value.ZdoId); if (zDO == null || IsDeadZdo(value.ZdoId)) { PendingDespawnDetachPersistRemovals.Add(value.ZdoId); continue; } PendingDespawnDetachPersistRemovals.Add(value.ZdoId); ApplyDetachPersist(value, zDO, nowSeconds); } foreach (ZDOID pendingDespawnDetachPersistRemoval in PendingDespawnDetachPersistRemovals) { PendingDespawnDetachPersists.Remove(pendingDespawnDetachPersistRemoval); } PendingDespawnDetachPersistRemovals.Clear(); } internal static void TryPersistDespawnCountdownBeforeResetZdo(ZNetView? nview) { //IL_0061: 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) Character val = default(Character); if (BossRulesPlugin.IsRuntimeServer() && BossRulesConfig.IsDespawnRulesEnabled() && !((Object)(object)nview == (Object)null) && nview.IsValid() && ((Component)nview).TryGetComponent(ref val) && !(val.GetHealth() <= 0f)) { ZDO zDO = nview.GetZDO(); if (zDO != null && ShouldQueueDespawnObservation(zDO.GetPrefab(), Utils.GetPrefabName(((Component)val).gameObject))) { PendingDespawnDetachPersists[zDO.m_uid] = new PendingDespawnDetachPersist(zDO.m_uid, val.GetCenterPoint(), zDO.GetPrefab(), Utils.GetPrefabName(((Component)val).gameObject)); } } } private static void ProcessTrackedDespawnTarget(ZDOID zdoId, ZDO zdo, TrackedDespawnState state, double nowSeconds) { //IL_0045: 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_0066: 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_006b: Unknown result type (might be due to invalid IL or missing references) //IL_006c: 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_0129: Unknown result type (might be due to invalid IL or missing references) //IL_013a: Unknown result type (might be due to invalid IL or missing references) //IL_0169: 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_0098: Unknown result type (might be due to invalid IL or missing references) //IL_01b5: Unknown result type (might be due to invalid IL or missing references) Character val = TryGetLoadedTrackedCharacter(zdo); if ((Object)(object)val != (Object)null && val.GetHealth() <= 0f) { PendingDespawnRemovals.Add(zdoId); return; } float effectiveRange = state.GetEffectiveRange(); float effectiveDelaySeconds = state.GetEffectiveDelaySeconds(); if (effectiveRange <= 0f) { state.ResetCountdown(); ScheduleTrackedDespawnCheck(zdoId, state, nowSeconds + GetIdleCheckIntervalSeconds()); return; } Vector3 val2 = (((Object)(object)val != (Object)null) ? val.GetCenterPoint() : zdo.GetPosition()); if (SceneProximityQueries.TryFindAnyLivingPlayerInRangeXZ(val2, effectiveRange, out var playerId)) { if (playerId != 0L) { state.LastInterestedPlayerId = playerId; } if (state.NoPlayerSince >= 0.0) { long playerId2 = playerId; if (SceneProximityQueries.TryFindNearestLivingPlayerInRangeXZ(val2, effectiveRange, out var playerId3)) { playerId2 = playerId3; } SendDespawnMessage(playerId2, BuildDespawnCanceledMessage(state.DisplayName)); } state.ResetCountdown(); ScheduleTrackedDespawnCheck(zdoId, state, nowSeconds + GetIdleCheckIntervalSeconds()); return; } if (state.NoPlayerSince < 0.0) { StartDespawnCountdown(state, nowSeconds, effectiveDelaySeconds); } double num = nowSeconds - state.NoPlayerSince; if (num >= (double)effectiveDelaySeconds) { IReadOnlyCollection refunds = ResolveRefundsForExecution(zdo, state); BossRulesDebugLog.Client($"Despawn executing prefab={state.PrefabName} zdo={zdoId} refunds={BossRulesDebugLog.FormatRefunds(refunds)} position={BossRulesDebugLog.FormatVector3(val2)}."); DespawnRefundExecutor.TryExecuteRefunds(val2, refunds); ApplyDespawnCleanupBeforeDestroy(zdo); zdo.SetOwner(ZDOMan.instance.m_sessionID); ZDOMan.instance.DestroyZDO(zdo); PendingDespawnRemovals.Add(zdoId); } else { int remainingSeconds = GetRemainingSeconds(effectiveDelaySeconds, num); if (state.CountdownRecipientPlayerId != 0L && remainingSeconds != state.LastAnnouncedRemainingSeconds && ShouldAnnounceDespawnRemaining(remainingSeconds)) { SendDespawnMessage(state.CountdownRecipientPlayerId, BuildDespawnReminderMessage(state.DisplayName, remainingSeconds)); state.LastAnnouncedRemainingSeconds = remainingSeconds; } ScheduleTrackedDespawnCheck(zdoId, state, nowSeconds + GetCountdownCheckIntervalSeconds()); } } private static IReadOnlyCollection ResolveRefundsForExecution(ZDO zdo, TrackedDespawnState state) { if (BossRulesRuntime.TryResolveDespawnTrackingRule(zdo, zdo.GetPrefab(), state.PrefabName, out string prefabName, out float? rangeOverride, out float? delayOverride, out IReadOnlyCollection refunds)) { state.PrefabName = (string.IsNullOrWhiteSpace(prefabName) ? state.PrefabName : prefabName); state.RangeOverride = rangeOverride; state.DelayOverride = delayOverride; state.Refunds.Clear(); if (refunds != null) { state.Refunds.AddRange(refunds); } } return state.Refunds; } private static void ApplyDespawnCleanupBeforeDestroy(ZDO zdo) { if (!((Object)(object)ZoneSystem.instance == (Object)null) && zdo.GetBool("bosscount", false)) { float num = default(float); ZoneSystem.instance.GetGlobalKey((GlobalKeys)38, ref num); ZoneSystem.instance.SetGlobalKey((GlobalKeys)38, Mathf.Max(0f, num - 1f)); zdo.Set("bosscount", false); } } private static void PruneTrackedDespawnTargetsAgainstCurrentConfig() { //IL_0024: 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_0057: Unknown result type (might be due to invalid IL or missing references) if (TrackedDespawnTargets.Count == 0) { return; } foreach (KeyValuePair trackedDespawnTarget in TrackedDespawnTargets) { ZDOID key = trackedDespawnTarget.Key; TrackedDespawnState value = trackedDespawnTarget.Value; if (!string.IsNullOrWhiteSpace(value.PrefabName) && !BossRulesRuntime.TryResolveDespawnTrackingRule(value.PrefabName, out float? _, out float? _, out IReadOnlyCollection _)) { PendingDespawnRemovals.Add(key); } } FlushPendingDespawnRemovals(); } private static void ApplyDetachPersist(PendingDespawnDetachPersist persist, ZDO zdo, double nowSeconds) { //IL_0002: 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_007d: Unknown result type (might be due to invalid IL or missing references) TrackedDespawnState trackedDespawnState = ApplyObservation(new PendingDespawnObservation(persist.ZdoId, persist.PrefabHashHint, persist.PrefabNameHint, DespawnObservationSource.LoadedCharacter), zdo); if (trackedDespawnState == null) { return; } float effectiveRange = trackedDespawnState.GetEffectiveRange(); if (!(effectiveRange <= 0f)) { if (SceneProximityQueries.TryFindAnyLivingPlayerInRangeXZ(persist.ProbePoint, effectiveRange, out var playerId)) { trackedDespawnState.LastInterestedPlayerId = playerId; } else if (!(trackedDespawnState.NoPlayerSince >= 0.0)) { float effectiveDelaySeconds = trackedDespawnState.GetEffectiveDelaySeconds(); StartDespawnCountdown(trackedDespawnState, nowSeconds, effectiveDelaySeconds); ScheduleTrackedDespawnCheck(persist.ZdoId, trackedDespawnState, nowSeconds); } } } private static void StartDespawnCountdown(TrackedDespawnState state, double nowSeconds, float despawnDelaySeconds) { state.NoPlayerSince = nowSeconds; state.CountdownRecipientPlayerId = GetCountdownRecipientPlayerId(state); state.LastAnnouncedRemainingSeconds = GetRemainingSeconds(despawnDelaySeconds, 0.0); if (despawnDelaySeconds > 0f && state.CountdownRecipientPlayerId != 0L) { SendDespawnMessage(state.CountdownRecipientPlayerId, BuildDespawnStartMessage(state.DisplayName, state.LastAnnouncedRemainingSeconds)); } } private static void PrimeTrackedDespawnInterestIfNeeded(TrackedDespawnState state, Vector3 point) { //IL_002a: Unknown result type (might be due to invalid IL or missing references) if (!(state.NoPlayerSince >= 0.0) && state.LastInterestedPlayerId == 0L) { float effectiveRange = state.GetEffectiveRange(); if (!(effectiveRange <= 0f) && SceneProximityQueries.TryFindAnyLivingPlayerInRangeXZ(point, effectiveRange, out var playerId)) { state.LastInterestedPlayerId = playerId; } } } private static long GetCountdownRecipientPlayerId(TrackedDespawnState state) { long lastInterestedPlayerId = state.LastInterestedPlayerId; if (!IsDespawnMessageRecipientAvailable(lastInterestedPlayerId)) { return 0L; } return lastInterestedPlayerId; } private static void FlushPendingDespawnRemovals() { //IL_001c: 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_0027: 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) if (PendingDespawnRemovals.Count == 0) { return; } foreach (ZDOID pendingDespawnRemoval in PendingDespawnRemovals) { if (TrackedDespawnTargets.TryGetValue(pendingDespawnRemoval, out TrackedDespawnState value)) { value.ScheduledCheckAtBucket = long.MinValue; } TrackedDespawnTargets.Remove(pendingDespawnRemoval); } PendingDespawnRemovals.Clear(); } private static TrackedDespawnState GetOrCreateTrackedDespawnState(ZDOID zdoId) { //IL_0005: 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) if (!TrackedDespawnTargets.TryGetValue(zdoId, out TrackedDespawnState value)) { value = new TrackedDespawnState(); TrackedDespawnTargets[zdoId] = value; } return value; } private static Character? TryGetLoadedTrackedCharacter(ZDO zdo) { if ((Object)(object)ZNetScene.instance == (Object)null || !ZNetScene.instance.m_instances.TryGetValue(zdo, out var value) || (Object)(object)value == (Object)null || (Object)(object)((Component)value).gameObject == (Object)null) { return null; } return ((Component)value).GetComponent(); } private static bool IsDeadZdo(ZDOID zdoId) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) if (ZDOMan.instance != null) { return ZDOMan.instance.m_deadZDOs.ContainsKey(zdoId); } return false; } private static int GetRemainingSeconds(float despawnDelaySeconds, double elapsedSeconds) { float num = Mathf.Max(0f, despawnDelaySeconds - (float)elapsedSeconds); return Mathf.Max(0, Mathf.CeilToInt(num)); } private static void ScheduleTrackedDespawnCheck(ZDOID zdoId, TrackedDespawnState state, double scheduledTime) { //IL_0030: Unknown result type (might be due to invalid IL or missing references) long key = (state.ScheduledCheckAtBucket = QuantizeScheduledCheck(scheduledTime)); if (!ScheduledDespawnChecks.TryGetValue(key, out List value)) { value = new List(); ScheduledDespawnChecks[key] = value; } value.Add(zdoId); } private static long QuantizeScheduledCheck(double scheduledTime) { return Math.Max(0L, (long)Math.Ceiling(scheduledTime * 1000.0)); } private static double GetIdleCheckIntervalSeconds() { return 1.0; } private static double GetCountdownCheckIntervalSeconds() { return 0.5; } private static double GetCurrentDespawnClockSeconds() { return DespawnClock.Elapsed.TotalSeconds; } private static bool ShouldAnnounceDespawnRemaining(int remainingSeconds) { if (remainingSeconds <= 0) { return false; } if (remainingSeconds <= 5) { return true; } return remainingSeconds % 5 == 0; } internal static void ConfigureMessages(string? despawnStart, string? despawnReminder, string? despawnCanceled) { _messageDespawnStart = despawnStart ?? "{name} will despawn in {seconds}s unless someone returns."; _messageDespawnReminder = despawnReminder ?? "{name} will despawn in {seconds}s."; _messageDespawnCanceled = despawnCanceled ?? "{name} despawn canceled."; } private static void SendDespawnMessage(long playerId, string message) { if (playerId == 0L || string.IsNullOrWhiteSpace(message)) { return; } if (BossRulesPlugin.IsRuntimeServer()) { if (!TrySendServerDespawnMessage(playerId, message)) { Player player = Player.GetPlayer(playerId); if (!((Object)(object)player == (Object)null)) { ((Character)player).Message((MessageType)1, message, 0, (Sprite)null); } } } else { Player player2 = Player.GetPlayer(playerId); if (!((Object)(object)player2 == (Object)null) && !((Object)(object)((Component)player2).gameObject == (Object)null) && !((Character)player2).IsDead()) { ((Character)player2).Message((MessageType)1, message, 0, (Sprite)null); } } } private static bool IsDespawnMessageRecipientAvailable(long recipientId) { if (recipientId == 0L) { return false; } if (BossRulesPlugin.IsRuntimeServer()) { if (IsValidMessageTargetPeerId(recipientId)) { return true; } Player player = Player.GetPlayer(recipientId); long targetPeerId; string resolutionSource; if ((Object)(object)player != (Object)null) { return TryResolveMessageTargetPeerId(player, out targetPeerId, out resolutionSource); } return false; } Player player2 = Player.GetPlayer(recipientId); if ((Object)(object)player2 != (Object)null && (Object)(object)((Component)player2).gameObject != (Object)null) { return !((Character)player2).IsDead(); } return false; } private static bool TrySendServerDespawnMessage(long recipientId, string message) { if (ZRoutedRpc.instance == null) { return false; } if (IsValidMessageTargetPeerId(recipientId)) { ZRoutedRpc.instance.InvokeRoutedRPC(recipientId, "ShowMessage", new object[2] { 1, message }); return true; } Player player = Player.GetPlayer(recipientId); if ((Object)(object)player != (Object)null && TryResolveMessageTargetPeerId(player, out long targetPeerId, out string _)) { ZRoutedRpc.instance.InvokeRoutedRPC(targetPeerId, "ShowMessage", new object[2] { 1, message }); return true; } return false; } private static bool TryResolveMessageTargetPeerId(Player player, out long targetPeerId, out string resolutionSource) { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //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) targetPeerId = 0L; resolutionSource = "none"; if ((Object)(object)player == (Object)null) { return false; } ZDOID zDOID = ((Character)player).GetZDOID(); long userID = ((ZDOID)(ref zDOID)).UserID; if (IsValidMessageTargetPeerId(userID)) { targetPeerId = userID; resolutionSource = "characterZdoUserId"; return true; } ZNet instance = ZNet.instance; List list = ((instance != null) ? instance.GetPeers() : null); if (list != null) { foreach (ZNetPeer item in list) { if (item != null && item.IsReady() && item.m_characterID == zDOID) { targetPeerId = item.m_uid; resolutionSource = "peerCharacterId"; return true; } } } string playerName = player.GetPlayerName(); if (!string.IsNullOrWhiteSpace(playerName)) { ZNet instance2 = ZNet.instance; ZNetPeer val = ((instance2 != null) ? instance2.GetPeerByPlayerName(playerName) : null); if (val != null && val.IsReady()) { targetPeerId = val.m_uid; resolutionSource = "playerName"; return true; } } return false; } private static bool IsValidMessageTargetPeerId(long peerId) { if (peerId == 0L) { return false; } if (peerId == ZNet.GetUID()) { return true; } ZNet instance = ZNet.instance; if (instance == null) { return false; } ZNetPeer peer = instance.GetPeer(peerId); return ((peer != null) ? new bool?(peer.IsReady()) : ((bool?)null)) == true; } private static string BuildDespawnStartMessage(string displayName, int remainingSeconds) { return FormatDespawnMessage(_messageDespawnStart, displayName, remainingSeconds); } private static string BuildDespawnReminderMessage(string displayName, int remainingSeconds) { return FormatDespawnMessage(_messageDespawnReminder, displayName, remainingSeconds); } private static string BuildDespawnCanceledMessage(string displayName) { return FormatDespawnMessage(_messageDespawnCanceled, displayName, 0); } private static string FormatDespawnMessage(string template, string displayName, int remainingSeconds) { return (template ?? "").Replace("{name}", displayName ?? "").Replace("{seconds}", remainingSeconds.ToString(CultureInfo.InvariantCulture)); } private static string GetDisplayName(Character? character) { if ((Object)(object)character == (Object)null) { return "Target"; } string hoverName = character.GetHoverName(); if (!string.IsNullOrWhiteSpace(hoverName)) { return hoverName; } if (!string.IsNullOrWhiteSpace(character.m_name)) { if (Localization.instance == null) { return character.m_name; } return Localization.instance.Localize(character.m_name); } if (!((Object)(object)((Component)character).gameObject != (Object)null) || string.IsNullOrWhiteSpace(((Object)((Component)character).gameObject).name)) { return "Target"; } return ((Object)((Component)character).gameObject).name; } internal static void MarkBootstrapScanDirty(string reason) { _pendingBootstrapScan = true; } private static void ObserveDespawnLookupVersion() { int despawnLookupVersion = BossRulesRuntime.GetDespawnLookupVersion(); if (despawnLookupVersion != _lastObservedDespawnLookupVersion) { _lastObservedDespawnLookupVersion = despawnLookupVersion; _pendingBootstrapScan = true; } } private static bool RunPendingBootstrapScan() { if (ZDOMan.instance == null) { return false; } IReadOnlyList despawnBootstrapPrefabOrder = BossRulesRuntime.GetDespawnBootstrapPrefabOrder(); if (despawnBootstrapPrefabOrder.Count == 0) { return true; } foreach (string item in despawnBootstrapPrefabOrder) { QueueBootstrapScanDespawnObservations(item); } return true; } internal static void QueueCreatedDespawnTarget(int prefabHashHint, ZDO? zdo) { //IL_0037: Unknown result type (might be due to invalid IL or missing references) if (BossRulesPlugin.IsRuntimeServer() && BossRulesConfig.IsDespawnRulesEnabled() && zdo != null && !((ZDOID)(ref zdo.m_uid)).IsNone()) { int num; int authoritativePrefabHash = (num = zdo.GetPrefab()); if (num == 0) { num = prefabHashHint; } if (GetCreatedZdoObservationDecision(authoritativePrefabHash, prefabHashHint) != CreatedZdoObservationDecision.Ineligible) { EnqueueDespawnObservation(new PendingDespawnObservation(zdo.m_uid, num, "", DespawnObservationSource.CreatedZdo)); } } } private static void ApplyPendingDespawnObservations() { //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Unknown result type (might be due to invalid IL or missing references) //IL_0089: 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_012e: Unknown result type (might be due to invalid IL or missing references) //IL_0163: Unknown result type (might be due to invalid IL or missing references) //IL_0168: 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) if (PendingDespawnObservations.Count == 0 || (Object)(object)ZNetScene.instance == (Object)null || (Object)(object)ObjectDB.instance == (Object)null || ZDOMan.instance == null) { return; } float time = Time.time; PendingDespawnObservationRemovals.Clear(); PendingDespawnObservationUpdates.Clear(); foreach (PendingDespawnObservation value in PendingDespawnObservations.Values) { if (value.NextAttemptAt > time) { continue; } ZDO zDO = ZDOMan.instance.GetZDO(value.ZdoId); if (zDO == null || IsDeadZdo(value.ZdoId)) { PendingDespawnObservationRemovals.Add(value.ZdoId); continue; } PendingDespawnObservation updatedObservation; ObservationApplicationResult observationApplicationResult = ApplyQueuedObservation(value, zDO, time, out updatedObservation); switch (observationApplicationResult) { case ObservationApplicationResult.Applied: case ObservationApplicationResult.Dropped: PendingDespawnObservationRemovals.Add(value.ZdoId); break; case ObservationApplicationResult.Deferred: PendingDespawnObservationUpdates.Add(updatedObservation); break; default: throw new ArgumentOutOfRangeException("applyResult", observationApplicationResult, null); } } foreach (PendingDespawnObservation pendingDespawnObservationUpdate in PendingDespawnObservationUpdates) { PendingDespawnObservations[pendingDespawnObservationUpdate.ZdoId] = pendingDespawnObservationUpdate; } foreach (ZDOID pendingDespawnObservationRemoval in PendingDespawnObservationRemovals) { PendingDespawnObservations.Remove(pendingDespawnObservationRemoval); } PendingDespawnObservationRemovals.Clear(); PendingDespawnObservationUpdates.Clear(); } internal static void TryTrackLoadedDespawnTarget(Character? character) { //IL_0062: 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 (!BossRulesPlugin.IsRuntimeServer() || !BossRulesConfig.IsDespawnRulesEnabled() || (Object)(object)character == (Object)null || (Object)(object)((Component)character).gameObject == (Object)null || character.IsDead()) { return; } ZNetView component = ((Component)character).GetComponent(); string prefabName = Utils.GetPrefabName(((Component)character).gameObject); if (!((Object)(object)component == (Object)null) && component.IsValid()) { ZDO zDO = component.GetZDO(); if (zDO != null && !IsDeadZdo(zDO.m_uid) && !string.IsNullOrWhiteSpace(prefabName) && ShouldQueueDespawnObservation(zDO.GetPrefab(), prefabName)) { EnqueueDespawnObservation(new PendingDespawnObservation(zDO.m_uid, zDO.GetPrefab(), prefabName, DespawnObservationSource.LoadedCharacter)); } } } private static void QueueBootstrapScanDespawnObservations(string prefabName) { //IL_0056: Unknown result type (might be due to invalid IL or missing references) if (string.IsNullOrWhiteSpace(prefabName) || ZDOMan.instance == null) { return; } BootstrapScanBuffer.Clear(); int num = 0; while (!ZDOMan.instance.GetAllZDOsWithPrefabIterative(prefabName, BootstrapScanBuffer, ref num)) { } foreach (ZDO item in BootstrapScanBuffer) { if (item != null && !((ZDOID)(ref item.m_uid)).IsNone()) { EnqueueDespawnObservation(new PendingDespawnObservation(item.m_uid, item.GetPrefab(), prefabName, DespawnObservationSource.BootstrapScan)); } } } private static void EnqueueDespawnObservation(PendingDespawnObservation observation) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: 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_0041: 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) ZDOID zdoId = observation.ZdoId; if (!((ZDOID)(ref zdoId)).IsNone()) { if (!PendingDespawnObservations.TryGetValue(observation.ZdoId, out var value)) { PendingDespawnObservations[observation.ZdoId] = observation; } else { PendingDespawnObservations[observation.ZdoId] = MergeDespawnObservation(value, observation); } } } private static PendingDespawnObservation MergeDespawnObservation(PendingDespawnObservation current, PendingDespawnObservation incoming) { //IL_00c7: Unknown result type (might be due to invalid IL or missing references) PendingDespawnObservation pendingDespawnObservation = ((GetObservationPriority(incoming.Source) >= GetObservationPriority(current.Source)) ? incoming : current); int prefabHashHint = ((incoming.PrefabHashHint != 0) ? incoming.PrefabHashHint : current.PrefabHashHint); string prefabNameHint = ((!string.IsNullOrWhiteSpace(incoming.PrefabNameHint)) ? incoming.PrefabNameHint : current.PrefabNameHint); float nextAttemptAt = ((pendingDespawnObservation.Source == DespawnObservationSource.CreatedZdo) ? Mathf.Max(current.NextAttemptAt, incoming.NextAttemptAt) : 0f); float expireAt = ((pendingDespawnObservation.Source == DespawnObservationSource.CreatedZdo) ? Mathf.Max(current.ExpireAt, incoming.ExpireAt) : 0f); int retryCount = ((pendingDespawnObservation.Source == DespawnObservationSource.CreatedZdo) ? Math.Max(current.RetryCount, incoming.RetryCount) : 0); return new PendingDespawnObservation(pendingDespawnObservation.ZdoId, prefabHashHint, prefabNameHint, pendingDespawnObservation.Source, nextAttemptAt, expireAt, retryCount); } private static int GetObservationPriority(DespawnObservationSource source) { return source switch { DespawnObservationSource.LoadedCharacter => 3, DespawnObservationSource.CreatedZdo => 2, _ => 1, }; } private static ObservationApplicationResult ApplyQueuedObservation(PendingDespawnObservation observation, ZDO zdo, float nowRealtime, out PendingDespawnObservation updatedObservation) { updatedObservation = observation; if (ApplyObservation(observation, zdo) != null) { return ObservationApplicationResult.Applied; } if (TryDeferCreatedZdoObservation(observation, zdo, nowRealtime, out updatedObservation)) { return ObservationApplicationResult.Deferred; } return ObservationApplicationResult.Dropped; } private static TrackedDespawnState? ApplyObservation(PendingDespawnObservation observation, ZDO zdo) { return ApplyObservation(zdo, observation.PrefabHashHint, observation.PrefabNameHint); } private static TrackedDespawnState? ApplyObservation(ZDO zdo, int prefabHashHint, string prefabNameHint) { if (!TryResolveObservationConfig(zdo, prefabHashHint, prefabNameHint, out string prefabName, out float? rangeOverride, out float? delayOverride, out IReadOnlyCollection refunds)) { return null; } return ApplyObservationResolved(prefabName, zdo, rangeOverride, delayOverride, refunds); } private static bool TryResolveObservationConfig(ZDO zdo, int prefabHashHint, string prefabNameHint, out string prefabName, out float? rangeOverride, out float? delayOverride, out IReadOnlyCollection refunds) { prefabName = (string.IsNullOrWhiteSpace(prefabNameHint) ? "" : prefabNameHint); rangeOverride = null; delayOverride = null; refunds = (IReadOnlyCollection)(object)Array.Empty(); int num = zdo.GetPrefab(); if (num == 0) { num = prefabHashHint; } return BossRulesRuntime.TryResolveDespawnTrackingRule(zdo, num, prefabName, out prefabName, out rangeOverride, out delayOverride, out refunds); } private static TrackedDespawnState ApplyObservationResolved(string prefabName, ZDO zdo, float? rangeOverride, float? delayOverride, IReadOnlyCollection refunds) { //IL_0001: 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_0029: 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_00e5: Unknown result type (might be due to invalid IL or missing references) TrackedDespawnState orCreateTrackedDespawnState = GetOrCreateTrackedDespawnState(zdo.m_uid); Character val = TryGetLoadedTrackedCharacter(zdo); if ((Object)(object)val != (Object)null) { orCreateTrackedDespawnState.UpdateFromCharacter(val, rangeOverride, delayOverride, refunds); PrimeTrackedDespawnInterestIfNeeded(orCreateTrackedDespawnState, val.GetCenterPoint()); } else { orCreateTrackedDespawnState.UpdateFromZdoPrefab(prefabName, rangeOverride, delayOverride, refunds); PrimeTrackedDespawnInterestIfNeeded(orCreateTrackedDespawnState, zdo.GetPosition()); } BossRulesDebugLog.Client(string.Format("Despawn tracking resolved prefab={0} zdo={1} loaded={2} range={3} delay={4} refunds={5}.", prefabName, zdo.m_uid, (Object)(object)val != (Object)null, rangeOverride.HasValue ? rangeOverride.Value.ToString("0.##", CultureInfo.InvariantCulture) : "", delayOverride.HasValue ? delayOverride.Value.ToString("0.##", CultureInfo.InvariantCulture) : "", BossRulesDebugLog.FormatRefunds(refunds))); ScheduleTrackedDespawnCheck(zdo.m_uid, orCreateTrackedDespawnState, GetCurrentDespawnClockSeconds()); return orCreateTrackedDespawnState; } private static CreatedZdoObservationDecision GetCreatedZdoObservationDecision(int authoritativePrefabHash, int prefabHashHint) { if (BossRulesRuntime.TryGetCachedDespawnTrackingPrefabHashEligibility((authoritativePrefabHash != 0) ? authoritativePrefabHash : prefabHashHint, out var eligible)) { if (!eligible) { return CreatedZdoObservationDecision.Ineligible; } return CreatedZdoObservationDecision.Eligible; } if (!BossRulesRuntime.IsDespawnTrackingRuleLookupReady()) { return CreatedZdoObservationDecision.Eligible; } return CreatedZdoObservationDecision.Unknown; } private static bool ShouldQueueDespawnObservation(int prefabHashHint, string prefabNameHint) { if (!BossRulesRuntime.IsDespawnTrackingRuleLookupReady()) { return true; } if (prefabHashHint != 0 && BossRulesRuntime.IsEligibleDespawnTrackingPrefabHash(prefabHashHint)) { return true; } if (!string.IsNullOrWhiteSpace(prefabNameHint)) { return BossRulesRuntime.IsEligibleDespawnTrackingPrefabName(prefabNameHint); } return false; } private static bool TryDeferCreatedZdoObservation(PendingDespawnObservation observation, ZDO zdo, float nowRealtime, out PendingDespawnObservation deferredObservation) { //IL_0043: Unknown result type (might be due to invalid IL or missing references) deferredObservation = observation; if (observation.Source != DespawnObservationSource.CreatedZdo || zdo.GetPrefab() != 0) { return false; } float num = ((observation.ExpireAt > 0f) ? observation.ExpireAt : (nowRealtime + 1f)); if (nowRealtime >= num) { return false; } deferredObservation = new PendingDespawnObservation(observation.ZdoId, observation.PrefabHashHint, observation.PrefabNameHint, observation.Source, nowRealtime + 0.25f, num, observation.RetryCount + 1); return true; } } internal static class DataForgeStatusEffectBridge { private const string ApiTypeName = "DataForge.DataForgeStatusEffectOwnership, DataForge"; private const float ResolveRetrySeconds = 2f; private static MethodInfo? HasActiveStatusEffectOverrideMethod; private static System.Reflection.EventInfo? StatusEffectOverridesWillApplyEvent; private static System.Reflection.EventInfo? StatusEffectOverridesAppliedEvent; private static Action? StatusEffectOverridesWillApplyHandler; private static Action? StatusEffectOverridesAppliedHandler; private static float NextResolveAt; private static bool Subscribed; private static bool InvokeWarningLogged; private static bool SubscriptionWarningLogged; internal static void ProcessDeferredSubscription() { if (!Subscribed && !(Time.realtimeSinceStartup < NextResolveAt)) { NextResolveAt = Time.realtimeSinceStartup + 2f; TryResolveAndSubscribe(); } } internal static bool IsStatusEffectOwnedByDataForge(string effectName) { if (string.IsNullOrWhiteSpace(effectName)) { return false; } ProcessDeferredSubscription(); MethodInfo hasActiveStatusEffectOverrideMethod = HasActiveStatusEffectOverrideMethod; if (hasActiveStatusEffectOverrideMethod == null) { return false; } try { object obj = hasActiveStatusEffectOverrideMethod.Invoke(null, new object[1] { effectName }); return obj is bool && (bool)obj; } catch (Exception ex) { if (!InvokeWarningLogged) { InvokeWarningLogged = true; BossRulesPlugin.BossRulesLogger.LogWarning((object)("Could not query DataForge status effect ownership. BossRules will ignore DataForge ownership until it can resolve again. " + ex.GetType().Name + ": " + ex.Message)); } HasActiveStatusEffectOverrideMethod = null; Subscribed = false; return false; } } internal static void Shutdown() { if (StatusEffectOverridesWillApplyEvent != null && StatusEffectOverridesWillApplyHandler != null) { try { StatusEffectOverridesWillApplyEvent.RemoveEventHandler(null, StatusEffectOverridesWillApplyHandler); } catch { } } if (StatusEffectOverridesAppliedEvent != null && StatusEffectOverridesAppliedHandler != null) { try { StatusEffectOverridesAppliedEvent.RemoveEventHandler(null, StatusEffectOverridesAppliedHandler); } catch { } } HasActiveStatusEffectOverrideMethod = null; StatusEffectOverridesWillApplyEvent = null; StatusEffectOverridesAppliedEvent = null; StatusEffectOverridesWillApplyHandler = null; StatusEffectOverridesAppliedHandler = null; Subscribed = false; } private static void TryResolveAndSubscribe() { Type type = Type.GetType("DataForge.DataForgeStatusEffectOwnership, DataForge", throwOnError: false); if (!(type == null)) { HasActiveStatusEffectOverrideMethod = type.GetMethod("HasActiveStatusEffectOverride", BindingFlags.Static | BindingFlags.Public, null, new Type[1] { typeof(string) }, null); StatusEffectOverridesWillApplyEvent = GetActionEvent(type, "StatusEffectOverridesWillApply"); StatusEffectOverridesAppliedEvent = GetActionEvent(type, "StatusEffectOverridesApplied"); if (HasActiveStatusEffectOverrideMethod == null) { LogSubscriptionWarningOnce("DataForge status effect ownership API was found, but the query method is missing."); return; } if (StatusEffectOverridesWillApplyEvent == null || StatusEffectOverridesAppliedEvent == null) { LogSubscriptionWarningOnce("DataForge status effect ownership API was found, but ownership events are missing or have unsupported signatures."); Subscribed = true; return; } StatusEffectOverridesWillApplyHandler = ForsakenPowerRuntime.ReleaseDataForgeOwnedSnapshots; StatusEffectOverridesAppliedHandler = ForsakenPowerRuntime.RequestReapply; StatusEffectOverridesWillApplyEvent.AddEventHandler(null, StatusEffectOverridesWillApplyHandler); StatusEffectOverridesAppliedEvent.AddEventHandler(null, StatusEffectOverridesAppliedHandler); Subscribed = true; BossRulesPlugin.BossRulesLogger.LogDebug((object)"Connected to DataForge status effect ownership API."); } } private static System.Reflection.EventInfo? GetActionEvent(Type apiType, string eventName) { System.Reflection.EventInfo eventInfo = apiType.GetEvent(eventName, BindingFlags.Static | BindingFlags.Public); if (!(eventInfo?.EventHandlerType == typeof(Action))) { return null; } return eventInfo; } private static void LogSubscriptionWarningOnce(string message) { if (!SubscriptionWarningLogged) { SubscriptionWarningLogged = true; BossRulesPlugin.BossRulesLogger.LogWarning((object)message); } } } internal static class ForsakenPowerConfiguration { private static readonly IDeserializer Deserializer = new DeserializerBuilder().WithNamingConvention(CamelCaseNamingConvention.Instance).Build(); internal static bool TryParse(string yaml, string source, out IReadOnlyList entries) { entries = Array.Empty(); try { List list = (List)(entries = Normalize(string.IsNullOrWhiteSpace(yaml) ? new List() : Deserializer.Deserialize>(yaml))); BossRulesPlugin.BossRulesLogger.LogInfo((object)$"Loaded forsaken power YAML from {source}: {list.Count} entries."); return true; } catch (Exception ex) { BossRulesPlugin.BossRulesLogger.LogError((object)("Rejected forsaken power YAML from " + source + ". Keeping the previous configuration. " + ex.GetType().Name + ": " + ex.Message)); return false; } } private static List Normalize(List? entries) { if (entries == null) { return new List(); } foreach (ForsakenPowerDefinition entry in entries) { entry.Effect = NormalizeOptionalString(entry.Effect); entry.Time = NormalizeOptionalString(entry.Time); entry.Attributes = NormalizeOptionalString(entry.Attributes); } return entries.Where((ForsakenPowerDefinition entry) => !string.IsNullOrWhiteSpace(entry.Effect)).ToList(); } private static string? NormalizeOptionalString(string? value) { string text = (value ?? "").Trim(); if (text.Length <= 0) { return null; } return text; } } internal static class ForsakenPowerConfigurationFiles { internal static void EnsureDefaultFile() { EnsureTextFile(BossRulesPlugin.ForsakenPowersYamlFilePath, BuildDefaultForsakenPowersYaml()); } private static void EnsureTextFile(string path, string content) { if (!File.Exists(path)) { File.WriteAllText(path, content); BossRulesPlugin.BossRulesLogger.LogInfo((object)("Created " + path + ".")); } } private static string BuildDefaultForsakenPowersYaml() { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine("# BossRules Forsaken Power overrides"); stringBuilder.AppendLine("# Uses the same compact field style as DataForge effects.yml, but only supports the fields listed here."); stringBuilder.AppendLine("# Rows can be copied directly into DataForge effects.yml. DataForge may support additional status effect fields."); stringBuilder.AppendLine("# If the same effect exists in DataForge effects.yml, DataForge owns that effect and BossRules skips it."); stringBuilder.AppendLine("# BossRules resets supported stat fields before applying each effect entry; list every supported effect value you want to keep."); stringBuilder.AppendLine("#"); stringBuilder.AppendLine("# Schema:"); stringBuilder.AppendLine("# - effect: GP_Eikthyr # GP_Eikthyr => override that Forsaken Power StatusEffect."); stringBuilder.AppendLine("# time: 18, 60 # 18, 60 => lasts 18s, then cannot reapply for 60s."); stringBuilder.AppendLine("# attributes: None # SailingPower => grant that StatusAttribute flag."); stringBuilder.AppendLine("# stats:"); stringBuilder.AppendLine("# regenMultiplier: 1, 1, 1 # 1.5, 0.5, 0 => health regen +50%, stamina regen x0.5, eitr regen disabled."); stringBuilder.AppendLine("# staminaDrainPerSec: 0 # 2 => drain 2 stamina per second."); stringBuilder.AppendLine("# adrenalineModifier: 0 # 0.25 => adrenaline gain/use value +25%."); stringBuilder.AppendLine("# speedModifier: 0 # 0.2 => movement speed +20%."); stringBuilder.AppendLine("# swimSpeedModifier: 0 # 0.3 => swim speed +30%."); stringBuilder.AppendLine("# jumpModifier: 0, 0, 0 # 0, 0.25, 0 => jump height +25%; 0.2, 0, 0.2 => jump distance +20%."); stringBuilder.AppendLine("# windRun: 0, 0 # 0.2, -0.25 => tailwind movement speed up to +20%, run stamina drain up to -25%."); stringBuilder.AppendLine("# armor: 0, 0 # 10, 0.25 => (armor + 10) * 1.25."); stringBuilder.AppendLine("# block: 0, 0 # 0.5, -5 => timed block/parry bonus +50%, block stamina cost -5 flat."); stringBuilder.AppendLine("# staggerModifier: 0 # -0.25 => stagger taken -25%."); stringBuilder.AppendLine("# addMaxCarryWeight: 0 # 100 => carry weight +100."); stringBuilder.AppendLine("# Skill values for skillLevel/skillLevel2: None, Swords, Knives, Clubs, Polearms, Spears, Blocking, Axes, Bows, ElementalMagic, BloodMagic, Unarmed, Pickaxes, WoodCutting, Crossbows, Jump, Sneak, Run, Swim, Fishing, Cooking, Farming, Crafting, Dodge, Ride."); stringBuilder.AppendLine("# skillLevel: None, 0 # Swords, 15 => treat Swords as +15 levels while active."); stringBuilder.AppendLine("# skillLevel2: None, 0 # Blocking, 10 => treat Blocking as +10 levels while active."); stringBuilder.AppendLine("# staminaDrainModifier:"); stringBuilder.AppendLine("# run: 0 # -0.25 => running stamina drain -25%."); stringBuilder.AppendLine("# attack: 0 # -0.2 => attack stamina cost -20%."); stringBuilder.AppendLine("# block: 0 # -0.2 => block stamina cost -20%."); stringBuilder.AppendLine("# dodge: 0 # -0.2 => dodge stamina cost -20%."); stringBuilder.AppendLine("# jump: 0 # -0.2 => jump stamina cost -20%."); stringBuilder.AppendLine("# sneak: 0 # -0.2 => sneak stamina cost -20%."); stringBuilder.AppendLine("# swim: 0 # -0.2 => swim stamina cost -20%."); stringBuilder.AppendLine("# homeItem: 0 # -0.2 => hammer/build stamina cost -20%."); stringBuilder.AppendLine("# damageTakenModifiers:"); stringBuilder.AppendLine("# blunt: Normal # Resistant => take 50% blunt damage; Normal => remove this effect's blunt modifier."); stringBuilder.AppendLine("# slash: Normal # Weak => take 150% slash damage."); stringBuilder.AppendLine("# pierce: Normal # VeryResistant => take 25% pierce damage."); stringBuilder.AppendLine("# chop: Normal # SlightlyWeak => take 125% chop damage."); stringBuilder.AppendLine("# pickaxe: Normal # Immune => take 0 pickaxe damage."); stringBuilder.AppendLine("# fire: Normal # Resistant => take 50% fire damage."); stringBuilder.AppendLine("# frost: Normal # VeryResistant => take 25% frost damage."); stringBuilder.AppendLine("# lightning: Normal # SlightlyResistant => take 75% lightning damage."); stringBuilder.AppendLine("# poison: Normal # Weak => take 150% poison damage."); stringBuilder.AppendLine("# spirit: Normal # Immune => take 0 spirit damage."); stringBuilder.AppendLine("# percentageDamageModifiers:"); stringBuilder.AppendLine("# blunt: 0 # 0.25 => blunt damage modifier +25%."); stringBuilder.AppendLine("# slash: 0 # 0.25 => slash damage modifier +25%."); stringBuilder.AppendLine("# pierce: 0 # 0.25 => pierce damage modifier +25%."); stringBuilder.AppendLine("# chop: 0 # 0.25 => chop damage modifier +25%."); stringBuilder.AppendLine("# pickaxe: 0 # 0.25 => pickaxe damage modifier +25%."); stringBuilder.AppendLine("# fire: 0 # 0.25 => fire damage modifier +25%."); stringBuilder.AppendLine("# frost: 0 # 0.25 => frost damage modifier +25%."); stringBuilder.AppendLine("# lightning: 0 # 0.25 => lightning damage modifier +25%."); stringBuilder.AppendLine("# poison: 0 # 0.25 => poison damage modifier +25%."); stringBuilder.AppendLine("# spirit: 0 # 0.25 => spirit damage modifier +25%."); stringBuilder.AppendLine("#"); stringBuilder.AppendLine("- effect: GP_Eikthyr"); stringBuilder.AppendLine(" time: 16, 60"); stringBuilder.AppendLine(" percentageDamageModifiers:"); stringBuilder.AppendLine(" Pickaxe: 0.5"); stringBuilder.AppendLine(" staminaDrainModifier:"); stringBuilder.AppendLine(" run: -0.5"); stringBuilder.AppendLine(" dodge: -0.5"); stringBuilder.AppendLine(" stats:"); stringBuilder.AppendLine(" speedModifier: 0.1"); stringBuilder.AppendLine(" damageTakenModifiers:"); stringBuilder.AppendLine(" Blunt: SlightlyResistant"); stringBuilder.AppendLine("- effect: GP_TheElder"); stringBuilder.AppendLine(" time: 16, 60"); stringBuilder.AppendLine(" percentageDamageModifiers:"); stringBuilder.AppendLine(" Chop: 0.5"); stringBuilder.AppendLine(" staminaDrainModifier:"); stringBuilder.AppendLine(" swim: -0.5"); stringBuilder.AppendLine(" sneak: -0.5"); stringBuilder.AppendLine(" stats:"); stringBuilder.AppendLine(" regenMultiplier: 2, 1, 1"); stringBuilder.AppendLine(" damageTakenModifiers:"); stringBuilder.AppendLine(" Poison: SlightlyResistant"); stringBuilder.AppendLine("- effect: GP_Bonemass"); stringBuilder.AppendLine(" time: 16, 60"); stringBuilder.AppendLine(" staminaDrainModifier:"); stringBuilder.AppendLine(" block: -0.5"); stringBuilder.AppendLine(" stats:"); stringBuilder.AppendLine(" block: 0, -5 # timed block bonus, flat block stamina cost. -5 returns 5 stamina."); stringBuilder.AppendLine(" armor: 20, 0.2"); stringBuilder.AppendLine(" damageTakenModifiers:"); stringBuilder.AppendLine(" Frost: SlightlyResistant"); stringBuilder.AppendLine("- effect: GP_Moder"); stringBuilder.AppendLine(" time: 16, 60"); stringBuilder.AppendLine(" attributes: SailingPower"); stringBuilder.AppendLine(" staminaDrainModifier:"); stringBuilder.AppendLine(" jump: -0.5"); stringBuilder.AppendLine(" stats:"); stringBuilder.AppendLine(" addMaxCarryWeight: 300"); stringBuilder.AppendLine(" jumpModifier: 0, 0.2, 0"); stringBuilder.AppendLine(" skillLevel: Farming, 25"); stringBuilder.AppendLine(" skillLevel2: Fishing, 25"); stringBuilder.AppendLine(" damageTakenModifiers:"); stringBuilder.AppendLine(" Fire: SlightlyResistant"); stringBuilder.AppendLine("- effect: GP_Yagluth"); stringBuilder.AppendLine(" time: 16, 60"); stringBuilder.AppendLine(" percentageDamageModifiers:"); stringBuilder.AppendLine(" Fire: 0.1"); stringBuilder.AppendLine(" Poison: 0.1"); stringBuilder.AppendLine(" Frost: 0.1"); stringBuilder.AppendLine(" Lightning: 0.1"); stringBuilder.AppendLine(" Spirit: 0.1"); stringBuilder.AppendLine(" stats:"); stringBuilder.AppendLine(" regenMultiplier: 1, 1, 2"); stringBuilder.AppendLine(" damageTakenModifiers:"); stringBuilder.AppendLine(" Pierce: SlightlyResistant"); stringBuilder.AppendLine("- effect: GP_Queen"); stringBuilder.AppendLine(" time: 16, 60"); stringBuilder.AppendLine(" percentageDamageModifiers:"); stringBuilder.AppendLine(" Pierce: 0.1"); stringBuilder.AppendLine(" Blunt: 0.1"); stringBuilder.AppendLine(" Slash: 0.1"); stringBuilder.AppendLine(" staminaDrainModifier:"); stringBuilder.AppendLine(" attack: -0.1"); stringBuilder.AppendLine(" damageTakenModifiers:"); stringBuilder.AppendLine(" Slash: SlightlyResistant"); stringBuilder.AppendLine("- effect: GP_Fader"); stringBuilder.AppendLine(" time: 16, 60"); stringBuilder.AppendLine(" stats:"); stringBuilder.AppendLine(" adrenalineModifier: 1"); stringBuilder.AppendLine(" staggerModifier: -0.5"); stringBuilder.AppendLine(" damageTakenModifiers:"); stringBuilder.AppendLine(" Lightning: SlightlyResistant"); return stringBuilder.ToString(); } } internal sealed class ForsakenPowerDefinition { [YamlMember(Order = 1)] public string? Effect { get; set; } [YamlMember(Order = 2)] public string? Time { get; set; } [YamlMember(Order = 3)] public string? Attributes { get; set; } [YamlMember(Order = 4)] public ForsakenPowerStatsDefinition? Stats { get; set; } [YamlMember(Order = 5)] public ForsakenPowerStaminaDrainModifierDefinition? StaminaDrainModifier { get; set; } [YamlMember(Order = 6)] public Dictionary? DamageTakenModifiers { get; set; } [YamlMember(Order = 7)] public Dictionary? PercentageDamageModifiers { get; set; } } internal sealed class ForsakenPowerStatsDefinition { [YamlMember(Order = 1)] public string? RegenMultiplier { get; set; } [YamlMember(Order = 2)] public float? StaminaDrainPerSec { get; set; } [YamlMember(Order = 3)] public float? AdrenalineModifier { get; set; } [YamlMember(Order = 4)] public float? SpeedModifier { get; set; } [YamlMember(Order = 5)] public float? SwimSpeedModifier { get; set; } [YamlMember(Order = 6)] public string? JumpModifier { get; set; } [YamlMember(Order = 7)] public string? WindRun { get; set; } [YamlMember(Order = 8)] public string? Armor { get; set; } [YamlMember(Order = 9)] public string? Block { get; set; } [YamlMember(Order = 10)] public float? StaggerModifier { get; set; } [YamlMember(Order = 11)] public float? AddMaxCarryWeight { get; set; } [YamlMember(Order = 12)] public string? SkillLevel { get; set; } [YamlMember(Order = 13)] public string? SkillLevel2 { get; set; } } internal sealed class ForsakenPowerStaminaDrainModifierDefinition { [YamlMember(Order = 1)] public float? Run { get; set; } [YamlMember(Order = 2)] public float? Attack { get; set; } [YamlMember(Order = 3)] public float? Block { get; set; } [YamlMember(Order = 4)] public float? Dodge { get; set; } [YamlMember(Order = 5)] public float? Jump { get; set; } [YamlMember(Order = 6)] public float? Sneak { get; set; } [YamlMember(Order = 7)] public float? Swim { get; set; } [YamlMember(Order = 8)] public float? HomeItem { get; set; } } internal static class ForsakenPowerRuntime { private sealed class ForsakenPowerSnapshot { public StatusEffect Effect { get; set; } public float Ttl { get; set; } public float Cooldown { get; set; } public StatusAttribute Attributes { get; set; } public float RunStaminaDrainModifier { get; set; } public float JumpStaminaUseModifier { get; set; } public float AttackStaminaUseModifier { get; set; } public float BlockStaminaUseModifier { get; set; } public float BlockStaminaUseFlatValue { get; set; } public float TimedBlockBonus { get; set; } public float DodgeStaminaUseModifier { get; set; } public float SwimStaminaUseModifier { get; set; } public float SneakStaminaUseModifier { get; set; } public float HomeItemStaminaUseModifier { get; set; } public float StaminaDrainPerSec { get; set; } public float HealthRegenMultiplier { get; set; } public float StaminaRegenMultiplier { get; set; } public float EitrRegenMultiplier { get; set; } public float AddArmor { get; set; } public float ArmorMultiplier { get; set; } public SkillType SkillLevel { get; set; } public float SkillLevelModifier { get; set; } public SkillType SkillLevel2 { get; set; } public float SkillLevelModifier2 { get; set; } public List Mods { get; set; } = new List(); public DamageTypes PercentigeDamageModifiers { get; set; } public float AddMaxCarryWeight { get; set; } public float SpeedModifier { get; set; } public float SwimSpeedModifier { get; set; } public Vector3 JumpModifier { get; set; } public float WindMovementModifier { get; set; } public float WindRunStaminaModifier { get; set; } public float AdrenalineModifier { get; set; } public float StaggerModifier { get; set; } } private static readonly object Sync = new object(); private static List? _definition; private static bool _pendingApply; private static bool _lastEnabled; private static string _lastAppliedSignature = ""; private static readonly Dictionary SnapshotsByHash = new Dictionary(); internal static void Configure(List? definition) { lock (Sync) { _definition = definition; _pendingApply = true; } } internal static void RequestReapply() { lock (Sync) { _pendingApply = true; } } internal static void ReleaseDataForgeOwnedSnapshots() { lock (Sync) { RestoreSnapshotsOwnedByDataForgeLocked(); } } internal static void Reset() { lock (Sync) { _definition = null; RestoreSnapshotsNotOwnedByDataForgeLocked(); _pendingApply = false; _lastAppliedSignature = ""; _lastEnabled = false; } } internal static void ProcessDeferredApply() { bool flag = BossRulesConfig.IsForsakenPowerRulesEnabled(); lock (Sync) { List definition = _definition; string b = (flag ? BuildDefinitionSignature(definition) : ""); if (!_pendingApply && _lastEnabled == flag && string.Equals(_lastAppliedSignature, b, StringComparison.Ordinal)) { return; } } if (!IsGameDataReady()) { return; } lock (Sync) { flag = BossRulesConfig.IsForsakenPowerRulesEnabled(); List definition = _definition; string b = (flag ? BuildDefinitionSignature(definition) : ""); RestoreSnapshotsNotOwnedByDataForgeLocked(); if (flag && definition != null && definition.Count > 0) { ApplyDefinitionLocked(definition); } _pendingApply = false; _lastEnabled = flag; _lastAppliedSignature = b; } } internal static bool TryOverrideGuardianPowerAdrenalineGain(Player? player, out float originalValue) { originalValue = player?.m_adrenalineGuardianPower ?? 0f; if ((Object)(object)player == (Object)null || !BossRulesConfig.IsForsakenPowerRulesEnabled()) { return false; } player.m_adrenalineGuardianPower = BossRulesConfig.GetGuardianPowerActivationAdrenaline(); return true; } private static void ApplyDefinitionLocked(List definition) { HashSet hashSet = new HashSet(StringComparer.OrdinalIgnoreCase); HashSet hashSet2 = new HashSet(StringComparer.OrdinalIgnoreCase); foreach (ForsakenPowerDefinition item in definition) { string text = (item.Effect ?? "").Trim(); if (text.Length == 0 || DataForgeStatusEffectBridge.IsStatusEffectOwnedByDataForge(text)) { continue; } if (!hashSet.Add(text)) { BossRulesRuntime.WarnInvalidEntry("forsakenPowers contains multiple entries for '" + text + "'. Later entries apply after earlier entries."); } StatusEffect val = ResolveStatusEffect(text); if ((Object)(object)val == (Object)null) { BossRulesRuntime.WarnInvalidEntry("forsakenPowers entry '" + text + "' references an unknown StatusEffect prefab."); continue; } EnsureSnapshotLocked(val); if (hashSet2.Add(text)) { ClearSupportedFields(val); } SE_Stats val2 = (SE_Stats)(object)((val is SE_Stats) ? val : null); ApplyStatusEffectFields(val, item, text); if ((Object)(object)val2 != (Object)null) { ApplyStats(val2, item, text); } else if (HasStatFields(item)) { BossRulesRuntime.WarnInvalidEntry("forsakenPowers entry '" + text + "' is not an SE_Stats StatusEffect. Numeric stat fields were ignored."); } } } private static void ApplyStatusEffectFields(StatusEffect statusEffect, ForsakenPowerDefinition power, string context) { //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) float[] array = ParseFloatTuple(power.Time, context, "time", 1, 2); if (array != null && array.Length > 0) { statusEffect.m_ttl = Mathf.Max(0f, array[0]); } if (array != null && array.Length > 1) { statusEffect.m_cooldown = Mathf.Max(0f, array[1]); } if (!string.IsNullOrWhiteSpace(power.Attributes)) { if (Enum.TryParse(power.Attributes, ignoreCase: true, out StatusAttribute result)) { statusEffect.m_attributes = result; return; } BossRulesRuntime.WarnInvalidEntry("forsakenPowers entry '" + context + "' has unsupported attributes value '" + power.Attributes + "'."); } } private static void ApplyStats(SE_Stats stats, ForsakenPowerDefinition power, string context) { ApplyStatsBlock(stats, power.Stats, context); ApplyStaminaDrainModifier(stats, power.StaminaDrainModifier); ApplyPercentageDamageModifiers(stats, power.PercentageDamageModifiers, context); ApplyDamageTakenModifiers(stats, power.DamageTakenModifiers, context); } private static void ApplyStatsBlock(SE_Stats stats, ForsakenPowerStatsDefinition? definition, string context) { if (definition != null) { float[] array = ParseFloatTuple(definition.RegenMultiplier, context, "stats.regenMultiplier", 1, 3); if (array != null && array.Length > 0) { stats.m_healthRegenMultiplier = Mathf.Max(0f, array[0]); stats.m_staminaRegenMultiplier = Mathf.Max(0f, (array.Length > 1) ? array[1] : 1f); stats.m_eitrRegenMultiplier = Mathf.Max(0f, (array.Length > 2) ? array[2] : 1f); } if (definition.StaminaDrainPerSec.HasValue) { stats.m_staminaDrainPerSec = definition.StaminaDrainPerSec.Value; } if (definition.AdrenalineModifier.HasValue) { stats.m_adrenalineModifier = definition.AdrenalineModifier.Value; } if (definition.SpeedModifier.HasValue) { stats.m_speedModifier = definition.SpeedModifier.Value; } if (definition.SwimSpeedModifier.HasValue) { stats.m_swimSpeedModifier = definition.SwimSpeedModifier.Value; } float[] array2 = ParseFloatTuple(definition.JumpModifier, context, "stats.jumpModifier", 1, 3); if (array2 != null && array2.Length > 0) { stats.m_jumpModifier.x = array2[0]; stats.m_jumpModifier.y = ((array2.Length > 1) ? array2[1] : 0f); stats.m_jumpModifier.z = ((array2.Length > 2) ? array2[2] : 0f); } float[] array3 = ParseFloatTuple(definition.WindRun, context, "stats.windRun", 1, 2); if (array3 != null && array3.Length > 0) { stats.m_windMovementModifier = array3[0]; stats.m_windRunStaminaModifier = ((array3.Length > 1) ? array3[1] : 0f); } float[] array4 = ParseFloatTuple(definition.Armor, context, "stats.armor", 1, 2); if (array4 != null && array4.Length > 0) { stats.m_addArmor = array4[0]; stats.m_armorMultiplier = ((array4.Length > 1) ? array4[1] : 0f); } float[] array5 = ParseFloatTuple(definition.Block, context, "stats.block", 1, 2); if (array5 != null && array5.Length > 0) { stats.m_timedBlockBonus = array5[0]; stats.m_blockStaminaUseFlatValue = ((array5.Length > 1) ? array5[1] : 0f); } if (definition.StaggerModifier.HasValue) { stats.m_staggerModifier = definition.StaggerModifier.Value; } if (definition.AddMaxCarryWeight.HasValue) { stats.m_addMaxCarryWeight = definition.AddMaxCarryWeight.Value; } ApplySkillLevel(stats, definition.SkillLevel, context, "stats.skillLevel", first: true); ApplySkillLevel(stats, definition.SkillLevel2, context, "stats.skillLevel2", first: false); } } private static void ApplyStaminaDrainModifier(SE_Stats stats, ForsakenPowerStaminaDrainModifierDefinition? definition) { if (definition != null) { if (definition.Run.HasValue) { stats.m_runStaminaDrainModifier = definition.Run.Value; } if (definition.Attack.HasValue) { stats.m_attackStaminaUseModifier = definition.Attack.Value; } if (definition.Block.HasValue) { stats.m_blockStaminaUseModifier = definition.Block.Value; } if (definition.Dodge.HasValue) { stats.m_dodgeStaminaUseModifier = definition.Dodge.Value; } if (definition.Jump.HasValue) { stats.m_jumpStaminaUseModifier = definition.Jump.Value; } if (definition.Sneak.HasValue) { stats.m_sneakStaminaUseModifier = definition.Sneak.Value; } if (definition.Swim.HasValue) { stats.m_swimStaminaUseModifier = definition.Swim.Value; } if (definition.HomeItem.HasValue) { stats.m_homeItemStaminaUseModifier = definition.HomeItem.Value; } } } private static void ApplyPercentageDamageModifiers(SE_Stats stats, Dictionary? values, string context) { if (values == null) { return; } foreach (KeyValuePair value in values) { string key = value.Key; if (!SetDamagePercent(ref stats.m_percentigeDamageModifiers, key, value.Value)) { BossRulesRuntime.WarnInvalidEntry("forsakenPowers entry '" + context + "' has unsupported percentageDamageModifiers type '" + key + "'."); } } } private static void ApplyDamageTakenModifiers(SE_Stats stats, Dictionary? values, string context) { //IL_0049: 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_00cb: Unknown result type (might be due to invalid IL or missing references) if (values == null) { return; } if (stats.m_mods == null) { stats.m_mods = new List(); } foreach (KeyValuePair value2 in values) { string key = value2.Key; string value = value2.Value; DamageModifier result2; if (!Enum.TryParse(key, ignoreCase: true, out DamageType result) || !IsIndividualDamageType(result)) { BossRulesRuntime.WarnInvalidEntry("forsakenPowers entry '" + context + "' has unsupported damageTakenModifiers type '" + key + "'."); } else if (!Enum.TryParse(value, ignoreCase: true, out result2)) { BossRulesRuntime.WarnInvalidEntry("forsakenPowers entry '" + context + "' has unsupported damageTakenModifiers modifier '" + value + "'."); } else { SetDamageModifier(stats.m_mods, result, result2); } } } private static void ApplySkillLevel(SE_Stats stats, string? value, string context, string fieldName, bool first) { //IL_0079: 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_012e: 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_011e: Unknown result type (might be due to invalid IL or missing references) if (string.IsNullOrWhiteSpace(value)) { return; } string text = value.Trim(); string[] array = text.Split(new char[1] { ',' }); if (array.Length != 2) { BossRulesRuntime.WarnInvalidEntry("forsakenPowers entry '" + context + "' has invalid " + fieldName + " value '" + text + "'. Use SkillName, amount."); return; } string text2 = array[0].Trim(); float result2; if (!Enum.TryParse(text2, ignoreCase: true, out SkillType result) || (int)result == 0) { BossRulesRuntime.WarnInvalidEntry("forsakenPowers entry '" + context + "' has unsupported " + fieldName + " skill '" + text2 + "'."); } else if (!float.TryParse(array[1].Trim(), NumberStyles.Float, CultureInfo.InvariantCulture, out result2)) { BossRulesRuntime.WarnInvalidEntry("forsakenPowers entry '" + context + "' has invalid " + fieldName + " amount '" + array[1].Trim() + "'."); } else if (first) { stats.m_skillLevel = result; stats.m_skillLevelModifier = result2; } else { stats.m_skillLevel2 = result; stats.m_skillLevelModifier2 = result2; } } private static bool SetDamagePercent(ref DamageTypes damageTypes, string rawType, float value) { switch (NormalizeKey(rawType)) { case "damage": damageTypes.m_damage = value; return true; case "blunt": damageTypes.m_blunt = value; return true; case "slash": damageTypes.m_slash = value; return true; case "pierce": damageTypes.m_pierce = value; return true; case "chop": damageTypes.m_chop = value; return true; case "pickaxe": case "pickaxes": damageTypes.m_pickaxe = value; return true; case "fire": damageTypes.m_fire = value; return true; case "frost": damageTypes.m_frost = value; return true; case "lightning": damageTypes.m_lightning = value; return true; case "poison": damageTypes.m_poison = value; return true; case "spirit": damageTypes.m_spirit = value; return true; default: return false; } } private static void SetDamageModifier(List mods, DamageType damageType, DamageModifier modifier) { //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_004c: 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_0052: 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_005b: 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_0068: 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_002a: 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_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) for (int i = 0; i < mods.Count; i++) { if (mods[i].m_type == damageType) { if ((int)modifier == 0) { mods.RemoveAt(i); return; } mods[i] = new DamageModPair { m_type = damageType, m_modifier = modifier }; return; } } if ((int)modifier != 0) { mods.Add(new DamageModPair { m_type = damageType, m_modifier = modifier }); } } private static bool IsIndividualDamageType(DamageType damageType) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0003: Invalid comparison between Unknown and I4 //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Invalid comparison between Unknown and I4 //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Invalid comparison between Unknown and I4 //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Invalid comparison between Unknown and I4 //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Invalid comparison between Unknown and I4 //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Invalid comparison between Unknown and I4 //IL_0009: 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_000d: Invalid comparison between Unknown and I4 //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Invalid comparison between Unknown and I4 //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Invalid comparison between Unknown and I4 //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Invalid comparison between Unknown and I4 //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Invalid comparison between Unknown and I4 //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Invalid comparison between Unknown and I4 if ((int)damageType <= 16) { if ((int)damageType <= 4) { if (damageType - 1 <= 1 || (int)damageType == 4) { goto IL_0049; } } else if ((int)damageType == 8 || (int)damageType == 16) { goto IL_0049; } } else if ((int)damageType <= 64) { if ((int)damageType == 32 || (int)damageType == 64) { goto IL_0049; } } else if ((int)damageType == 128 || (int)damageType == 256 || (int)damageType == 512) { goto IL_0049; } return false; IL_0049: return true; } private static StatusEffect? ResolveStatusEffect(string prefab) { string normalized = (prefab ?? "").Trim(); if (normalized.Length == 0) { return null; } ObjectDB instance = ObjectDB.instance; StatusEffect val = ((instance != null) ? instance.GetStatusEffect(StringExtensionMethods.GetStableHashCode(normalized)) : null); if ((Object)(object)val != (Object)null) { return val; } return ((IEnumerable)ObjectDB.instance?.m_StatusEffects).FirstOrDefault((Func)((StatusEffect effect) => (Object)(object)effect != (Object)null && string.Equals(Utils.GetPrefabName(((Object)effect).name), normalized, StringComparison.OrdinalIgnoreCase))); } private static bool HasStatFields(ForsakenPowerDefinition power) { if (power.Stats == null && power.StaminaDrainModifier == null) { Dictionary damageTakenModifiers = power.DamageTakenModifiers; if (damageTakenModifiers == null || damageTakenModifiers.Count <= 0) { Dictionary percentageDamageModifiers = power.PercentageDamageModifiers; if (percentageDamageModifiers != null) { return percentageDamageModifiers.Count > 0; } return false; } } return true; } private static bool IsGameDataReady() { List list = ObjectDB.instance?.m_StatusEffects; if (list != null) { return list.Count > 0; } return false; } private static string BuildDefinitionSignature(List? definition) { if (definition == null) { return ""; } return string.Join("\n", definition.Select((ForsakenPowerDefinition power) => string.Join("|", power.Effect ?? "", power.Time ?? "", power.Attributes ?? "", (power.Stats != null) ? "stats" : "", (power.StaminaDrainModifier != null) ? "stamina" : "", power.DamageTakenModifiers?.Count.ToString(CultureInfo.InvariantCulture) ?? "", power.PercentageDamageModifiers?.Count.ToString(CultureInfo.InvariantCulture) ?? ""))); } private static float[]? ParseFloatTuple(string? rawValue, string context, string fieldName, int minCount, int maxCount) { if (string.IsNullOrWhiteSpace(rawValue)) { return null; } string text = rawValue.Trim(); string[] array = text.Split(new char[1] { ',' }); if (array.Length < minCount || array.Length > maxCount) { BossRulesRuntime.WarnInvalidEntry($"forsakenPowers entry '{context}' has invalid {fieldName} value '{text}'. Expected {minCount}~{maxCount} comma-separated number(s)."); return null; } float[] array2 = new float[array.Length]; for (int i = 0; i < array.Length; i++) { string text2 = array[i].Trim(); if (!float.TryParse(text2, NumberStyles.Float, CultureInfo.InvariantCulture, out array2[i])) { BossRulesRuntime.WarnInvalidEntry("forsakenPowers entry '" + context + "' has invalid " + fieldName + " number '" + text2 + "'."); return null; } } return array2; } private static string NormalizeKey(string? value) { return (value ?? "").Trim().Replace("_", "").Replace("-", "") .ToLowerInvariant(); } private static void EnsureSnapshotLocked(StatusEffect effect) { //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_0145: Unknown result type (might be due to invalid IL or missing references) //IL_015d: 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_01c5: Unknown result type (might be due to invalid IL or missing references) int key = effect.NameHash(); if (!SnapshotsByHash.ContainsKey(key)) { ForsakenPowerSnapshot value = new ForsakenPowerSnapshot { Effect = effect, Ttl = effect.m_ttl, Cooldown = effect.m_cooldown, Attributes = effect.m_attributes }; SE_Stats val = (SE_Stats)(object)((effect is SE_Stats) ? effect : null); if (val != null) { value = new ForsakenPowerSnapshot { Effect = effect, Ttl = effect.m_ttl, Cooldown = effect.m_cooldown, Attributes = effect.m_attributes, RunStaminaDrainModifier = val.m_runStaminaDrainModifier, JumpStaminaUseModifier = val.m_jumpStaminaUseModifier, AttackStaminaUseModifier = val.m_attackStaminaUseModifier, BlockStaminaUseModifier = val.m_blockStaminaUseModifier, BlockStaminaUseFlatValue = val.m_blockStaminaUseFlatValue, TimedBlockBonus = val.m_timedBlockBonus, DodgeStaminaUseModifier = val.m_dodgeStaminaUseModifier, SwimStaminaUseModifier = val.m_swimStaminaUseModifier, SneakStaminaUseModifier = val.m_sneakStaminaUseModifier, HomeItemStaminaUseModifier = val.m_homeItemStaminaUseModifier, StaminaDrainPerSec = val.m_staminaDrainPerSec, HealthRegenMultiplier = val.m_healthRegenMultiplier, StaminaRegenMultiplier = val.m_staminaRegenMultiplier, EitrRegenMultiplier = val.m_eitrRegenMultiplier, AddArmor = val.m_addArmor, ArmorMultiplier = val.m_armorMultiplier, SkillLevel = val.m_skillLevel, SkillLevelModifier = val.m_skillLevelModifier, SkillLevel2 = val.m_skillLevel2, SkillLevelModifier2 = val.m_skillLevelModifier2, Mods = ((val.m_mods != null) ? new List(val.m_mods) : new List()), PercentigeDamageModifiers = val.m_percentigeDamageModifiers, AddMaxCarryWeight = val.m_addMaxCarryWeight, SpeedModifier = val.m_speedModifier, SwimSpeedModifier = val.m_swimSpeedModifier, JumpModifier = val.m_jumpModifier, WindMovementModifier = val.m_windMovementModifier, WindRunStaminaModifier = val.m_windRunStaminaModifier, AdrenalineModifier = val.m_adrenalineModifier, StaggerModifier = val.m_staggerModifier }; } SnapshotsByHash[key] = value; } } private static void RestoreSnapshotsNotOwnedByDataForgeLocked() { foreach (ForsakenPowerSnapshot value in SnapshotsByHash.Values) { if (!DataForgeStatusEffectBridge.IsStatusEffectOwnedByDataForge(Utils.GetPrefabName(((Object)value.Effect).name))) { RestoreSnapshot(value); } } } private static void RestoreSnapshotsOwnedByDataForgeLocked() { foreach (ForsakenPowerSnapshot value in SnapshotsByHash.Values) { if (DataForgeStatusEffectBridge.IsStatusEffectOwnedByDataForge(Utils.GetPrefabName(((Object)value.Effect).name))) { RestoreSnapshot(value); } } } private static void RestoreSnapshot(ForsakenPowerSnapshot snapshot) { //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0102: Unknown result type (might be due to invalid IL or missing references) //IL_0107: Unknown result type (might be due to invalid IL or missing references) //IL_011a: Unknown result type (might be due to invalid IL or missing references) //IL_011f: Unknown result type (might be due to invalid IL or missing references) //IL_0143: 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_0173: Unknown result type (might be due to invalid IL or missing references) //IL_0178: Unknown result type (might be due to invalid IL or missing references) StatusEffect effect = snapshot.Effect; if (!((Object)(object)effect == (Object)null)) { effect.m_ttl = snapshot.Ttl; effect.m_cooldown = snapshot.Cooldown; effect.m_attributes = snapshot.Attributes; SE_Stats val = (SE_Stats)(object)((effect is SE_Stats) ? effect : null); if (val != null) { val.m_runStaminaDrainModifier = snapshot.RunStaminaDrainModifier; val.m_jumpStaminaUseModifier = snapshot.JumpStaminaUseModifier; val.m_attackStaminaUseModifier = snapshot.AttackStaminaUseModifier; val.m_blockStaminaUseModifier = snapshot.BlockStaminaUseModifier; val.m_blockStaminaUseFlatValue = snapshot.BlockStaminaUseFlatValue; val.m_timedBlockBonus = snapshot.TimedBlockBonus; val.m_dodgeStaminaUseModifier = snapshot.DodgeStaminaUseModifier; val.m_swimStaminaUseModifier = snapshot.SwimStaminaUseModifier; val.m_sneakStaminaUseModifier = snapshot.SneakStaminaUseModifier; val.m_homeItemStaminaUseModifier = snapshot.HomeItemStaminaUseModifier; val.m_staminaDrainPerSec = snapshot.StaminaDrainPerSec; val.m_healthRegenMultiplier = snapshot.HealthRegenMultiplier; val.m_staminaRegenMultiplier = snapshot.StaminaRegenMultiplier; val.m_eitrRegenMultiplier = snapshot.EitrRegenMultiplier; val.m_addArmor = snapshot.AddArmor; val.m_armorMultiplier = snapshot.ArmorMultiplier; val.m_skillLevel = snapshot.SkillLevel; val.m_skillLevelModifier = snapshot.SkillLevelModifier; val.m_skillLevel2 = snapshot.SkillLevel2; val.m_skillLevelModifier2 = snapshot.SkillLevelModifier2; val.m_mods = new List(snapshot.Mods); val.m_percentigeDamageModifiers = snapshot.PercentigeDamageModifiers; val.m_addMaxCarryWeight = snapshot.AddMaxCarryWeight; val.m_speedModifier = snapshot.SpeedModifier; val.m_swimSpeedModifier = snapshot.SwimSpeedModifier; val.m_jumpModifier = snapshot.JumpModifier; val.m_windMovementModifier = snapshot.WindMovementModifier; val.m_windRunStaminaModifier = snapshot.WindRunStaminaModifier; val.m_adrenalineModifier = snapshot.AdrenalineModifier; val.m_staggerModifier = snapshot.StaggerModifier; } } } private static void ClearSupportedFields(StatusEffect effect) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: Unknown result type (might be due to invalid IL or missing references) //IL_00d6: Unknown result type (might be due to invalid IL or missing references) //IL_00f7: Unknown result type (might be due to invalid IL or missing references) //IL_011f: Unknown result type (might be due to invalid IL or missing references) //IL_0124: Unknown result type (might be due to invalid IL or missing references) effect.m_attributes = (StatusAttribute)0; SE_Stats val = (SE_Stats)(object)((effect is SE_Stats) ? effect : null); if (val != null) { val.m_runStaminaDrainModifier = 0f; val.m_jumpStaminaUseModifier = 0f; val.m_attackStaminaUseModifier = 0f; val.m_blockStaminaUseModifier = 0f; val.m_blockStaminaUseFlatValue = 0f; val.m_timedBlockBonus = 0f; val.m_dodgeStaminaUseModifier = 0f; val.m_swimStaminaUseModifier = 0f; val.m_sneakStaminaUseModifier = 0f; val.m_homeItemStaminaUseModifier = 0f; val.m_staminaDrainPerSec = 0f; val.m_healthRegenMultiplier = 1f; val.m_staminaRegenMultiplier = 1f; val.m_eitrRegenMultiplier = 1f; val.m_addArmor = 0f; val.m_armorMultiplier = 0f; val.m_skillLevel = (SkillType)0; val.m_skillLevelModifier = 0f; val.m_skillLevel2 = (SkillType)0; val.m_skillLevelModifier2 = 0f; val.m_mods = new List(); val.m_percentigeDamageModifiers = default(DamageTypes); val.m_addMaxCarryWeight = 0f; val.m_speedModifier = 0f; val.m_swimSpeedModifier = 0f; val.m_jumpModifier = Vector3.zero; val.m_windMovementModifier = 0f; val.m_windRunStaminaModifier = 0f; val.m_adrenalineModifier = 0f; val.m_staggerModifier = 0f; } } } [HarmonyPatch(typeof(Player), "ActivateGuardianPower")] internal static class PlayerActivateGuardianPowerForsakenPowerPatch { private static void Prefix(Player __instance, out float __state) { if (!ForsakenPowerRuntime.TryOverrideGuardianPowerAdrenalineGain(__instance, out __state)) { __state = float.NaN; } } private static void Postfix(Player __instance, float __state) { if (!float.IsNaN(__state)) { __instance.m_adrenalineGuardianPower = __state; } } } internal static class ForsakenPowerSelectionRuntime { private const float GuardianPowerHintBaseOffsetDown = 8f; private const float GuardianPowerHintFixedExtraOffsetDown = 8f; private static readonly Dictionary GuardianPowerOrder = new Dictionary(StringComparer.OrdinalIgnoreCase) { { "GP_Eikthyr", 0 }, { "GP_TheElder", 1 }, { "GP_Bonemass", 2 }, { "GP_Moder", 3 }, { "GP_Yagluth", 4 }, { "GP_Queen", 5 }, { "GP_Fader", 6 }, { "GP_DeepNorth", 7 }, { "SE_Boss_Brutalis", 8 }, { "SE_Boss_Gorr", 9 }, { "SE_Boss_StormHerald", 10 }, { "SE_Boss_Sythrak", 11 } }; private static TMP_Text? _guardianPowerHintText; private static int _guardianPowerHintHudInstanceId; private static readonly List CachedUnlockedGuardianPowers = new List(); private static int _cachedUnlockedGuardianPowersPlayerInstanceId; private static bool _unlockedGuardianPowersDirty = true; private static KeyboardShortcut _cachedHintShortcut; private static string _cachedHintRotateLabel = ""; private static string _cachedHintText = ""; internal static void InvalidateGuardianPowerCache() { _unlockedGuardianPowersDirty = true; } internal static void TryRotateSelection(Player? player) { if (!CanRotateSelection(player)) { return; } List cachedUnlockedGuardianPowers = GetCachedUnlockedGuardianPowers(player); if (cachedUnlockedGuardianPowers.Count == 0) { ((Character)player).Message((MessageType)1, "No unlocked Forsaken Powers available.", 0, (Sprite)null); return; } string currentPower = player.GetGuardianPowerName() ?? ""; if (cachedUnlockedGuardianPowers.Count != 1 || !string.Equals(cachedUnlockedGuardianPowers[0], currentPower, StringComparison.OrdinalIgnoreCase)) { int num = cachedUnlockedGuardianPowers.FindIndex((string powerName) => string.Equals(powerName, currentPower, StringComparison.OrdinalIgnoreCase)); int index = ((num >= 0) ? ((num + 1) % cachedUnlockedGuardianPowers.Count) : 0); string text = cachedUnlockedGuardianPowers[index]; player.SetGuardianPower(text); ((Character)player).Message((MessageType)1, "Forsaken Power: " + GetDisplayName(text), 0, (Sprite)null); } } internal static void UpdateHudHint(Hud? hud, Player? player) { if ((Object)(object)hud == (Object)null) { return; } if (!ShouldShowHudHint(hud, player)) { HideGuardianPowerHint(hud); return; } TMP_Text val = EnsureGuardianPowerHint(hud); if (!((Component)val).gameObject.activeSelf) { ((Component)val).gameObject.SetActive(true); } ApplyGuardianPowerHintLayout(hud, val); string cachedHintText = GetCachedHintText(); if (!string.Equals(val.text, cachedHintText, StringComparison.Ordinal)) { val.text = cachedHintText; } } private static bool CanRotateSelection(Player? player) { //IL_001d: Unknown result type (might be due to invalid IL or missing references) if (!BossRulesConfig.IsRemoteForsakenPowerSelectionEnabled() || (Object)(object)player == (Object)null || (Object)(object)player != (Object)(object)Player.m_localPlayer || !IsShortcutDown(BossRulesConfig.GetRotateForsakenPowerShortcut())) { return false; } if (((Character)player).IsDead() || ((Character)player).InCutscene() || ((Character)player).IsTeleporting()) { return false; } if (((Object)(object)Chat.instance != (Object)null && Chat.instance.HasFocus()) || Console.IsVisible() || TextInput.IsVisible() || StoreGui.IsVisible() || InventoryGui.IsVisible() || Menu.IsVisible() || ((Object)(object)TextViewer.instance != (Object)null && TextViewer.instance.IsVisible()) || Minimap.IsOpen() || Minimap.InTextInput() || GameCamera.InFreeFly() || PlayerCustomizaton.IsBarberGuiVisible() || UnifiedPopup.IsVisible() || Hud.InRadial() || Hud.IsPieceSelectionVisible()) { return false; } return true; } private static List BuildUnlockedGuardianPowers(Player player) { List unlockedGuardianPowerNames = BossStonePerPlayerRuntime.GetUnlockedGuardianPowerNames(player); unlockedGuardianPowerNames.RemoveAll(delegate(string powerName) { if (powerName.Length != 0) { ObjectDB instance = ObjectDB.instance; return (Object)(object)((instance != null) ? instance.GetStatusEffect(StringExtensionMethods.GetStableHashCode(powerName)) : null) == (Object)null; } return true; }); unlockedGuardianPowerNames.Sort(CompareGuardianPowerNames); return unlockedGuardianPowerNames; } private static List GetCachedUnlockedGuardianPowers(Player player) { int instanceID = ((Object)player).GetInstanceID(); if (_unlockedGuardianPowersDirty || _cachedUnlockedGuardianPowersPlayerInstanceId != instanceID) { CachedUnlockedGuardianPowers.Clear(); CachedUnlockedGuardianPowers.AddRange(BuildUnlockedGuardianPowers(player)); _cachedUnlockedGuardianPowersPlayerInstanceId = instanceID; _unlockedGuardianPowersDirty = false; } return CachedUnlockedGuardianPowers; } private static int CompareGuardianPowerNames(string left, string right) { int guardianPowerOrder = GetGuardianPowerOrder(left); int guardianPowerOrder2 = GetGuardianPowerOrder(right); int num = guardianPowerOrder.CompareTo(guardianPowerOrder2); if (num != 0) { return num; } return string.Compare(left, right, StringComparison.OrdinalIgnoreCase); } private static int GetGuardianPowerOrder(string guardianPowerName) { if (!GuardianPowerOrder.TryGetValue(guardianPowerName ?? "", out var value)) { return int.MaxValue; } return value; } private static string GetDisplayName(string guardianPowerName) { ObjectDB instance = ObjectDB.instance; string text = ((instance == null) ? null : instance.GetStatusEffect(StringExtensionMethods.GetStableHashCode(guardianPowerName))?.m_name) ?? guardianPowerName; if (Localization.instance == null) { return text; } return Localization.instance.Localize(text); } private static bool ShouldShowHudHint(Hud hud, Player? player) { //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_0025: Unknown result type (might be due to invalid IL or missing references) KeyboardShortcut rotateForsakenPowerShortcut = BossRulesConfig.GetRotateForsakenPowerShortcut(); if (!BossRulesConfig.IsRemoteForsakenPowerSelectionEnabled() || (Object)(object)player == (Object)null || (Object)(object)player != (Object)(object)Player.m_localPlayer || (int)((KeyboardShortcut)(ref rotateForsakenPowerShortcut)).MainKey == 0 || (Object)(object)hud.m_gpRoot == (Object)null || !((Component)hud.m_gpRoot).gameObject.activeSelf) { return false; } return GetCachedUnlockedGuardianPowers(player).Count > 0; } private static TMP_Text EnsureGuardianPowerHint(Hud hud) { if ((Object)(object)_guardianPowerHintText != (Object)null && _guardianPowerHintHudInstanceId == ((Object)hud).GetInstanceID() && (Object)(object)((Component)_guardianPowerHintText).gameObject != (Object)null) { return _guardianPowerHintText; } TMP_Text val = (((Object)(object)hud.m_gpCooldown != (Object)null) ? hud.m_gpCooldown : hud.m_gpName); _guardianPowerHintText = Object.Instantiate(val, (Transform)(object)hud.m_gpRoot); ((Object)_guardianPowerHintText).name = "BossRules_ForsakenRotateHint"; _guardianPowerHintText.text = ""; _guardianPowerHintText.textWrappingMode = (TextWrappingModes)0; _guardianPowerHintText.fontSize = Mathf.Max(12f, val.fontSize - 2f); _guardianPowerHintText.alignment = (TextAlignmentOptions)514; ((Transform)_guardianPowerHintText.rectTransform).SetParent((Transform)(object)hud.m_gpRoot, false); _guardianPowerHintHudInstanceId = ((Object)hud).GetInstanceID(); return _guardianPowerHintText; } private static void HideGuardianPowerHint(Hud hud) { if ((Object)(object)_guardianPowerHintText != (Object)null && _guardianPowerHintHudInstanceId == ((Object)hud).GetInstanceID() && (Object)(object)((Component)_guardianPowerHintText).gameObject != (Object)null && ((Component)_guardianPowerHintText).gameObject.activeSelf) { ((Component)_guardianPowerHintText).gameObject.SetActive(false); } } private static void ApplyGuardianPowerHintLayout(Hud hud, TMP_Text hintText) { //IL_0015: 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_004d: 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_0078: Unknown result type (might be due to invalid IL or missing references) //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) //IL_00a2: 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) RectTransform rectTransform = hintText.rectTransform; RectTransform rectTransform2 = ((Graphic)hud.m_gpIcon).rectTransform; rectTransform.anchorMin = rectTransform2.anchorMin; rectTransform.anchorMax = rectTransform2.anchorMax; rectTransform.pivot = new Vector2(0.5f, 1f); float x = rectTransform2.anchoredPosition.x; float y = rectTransform2.anchoredPosition.y; Rect rect = rectTransform2.rect; rectTransform.anchoredPosition = new Vector2(x, y - ((Rect)(ref rect)).height * 0.5f - 8f - 8f); rect = rectTransform2.rect; rectTransform.sizeDelta = new Vector2(Mathf.Max(((Rect)(ref rect)).width + 72f, 128f), rectTransform.sizeDelta.y); } private static string GetCachedHintText() { //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_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_004f: 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) KeyboardShortcut rotateForsakenPowerShortcut = BossRulesConfig.GetRotateForsakenPowerShortcut(); string forsakenPowerRotateLabel = BossRulesRuntime.GetForsakenPowerRotateLabel(); if (_cachedHintText.Length != 0) { object obj = rotateForsakenPowerShortcut; if (((object)Unsafe.As(ref _cachedHintShortcut)/*cast due to .constrained prefix*/).Equals(obj) && string.Equals(_cachedHintRotateLabel, forsakenPowerRotateLabel, StringComparison.Ordinal)) { goto IL_0065; } } _cachedHintShortcut = rotateForsakenPowerShortcut; _cachedHintRotateLabel = forsakenPowerRotateLabel; _cachedHintText = "[" + GetShortcutLabel(rotateForsakenPowerShortcut) + "] " + forsakenPowerRotateLabel; goto IL_0065; IL_0065: return _cachedHintText; } private static string GetShortcutLabel(KeyboardShortcut shortcut) { //IL_002f: 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) List list = ((KeyboardShortcut)(ref shortcut)).Modifiers.Select(GetKeyLabel).ToList(); if ((int)((KeyboardShortcut)(ref shortcut)).MainKey != 0) { list.Add(GetKeyLabel(((KeyboardShortcut)(ref shortcut)).MainKey)); } return string.Join("+", list.Where((string label) => label.Length > 0)); } private unsafe static string GetKeyLabel(KeyCode keyCode) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0003: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Expected I4, but got Unknown //IL_0031: 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_0055: Expected I4, but got Unknown return (keyCode - 48) switch { 0 => "0", 1 => "1", 2 => "2", 3 => "3", 4 => "4", 5 => "5", 6 => "6", 7 => "7", 8 => "8", 9 => "9", _ => (keyCode - 303) switch { 3 => "Ctrl", 2 => "Ctrl", 1 => "Shift", 0 => "Shift", 5 => "Alt", 4 => "Alt", _ => ((object)(*(KeyCode*)(&keyCode))/*cast due to .constrained prefix*/).ToString(), }, }; } private static bool IsShortcutDown(KeyboardShortcut shortcut) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) if ((int)((KeyboardShortcut)(ref shortcut)).MainKey != 0 && Input.GetKeyDown(((KeyboardShortcut)(ref shortcut)).MainKey)) { return ((KeyboardShortcut)(ref shortcut)).Modifiers.All((Func)Input.GetKey); } return false; } } internal sealed class IntRangeDefinition : IYamlConvertible { public int? Min { get; set; } public int? Max { get; set; } internal bool HasValues() { if (!Min.HasValue) { return Max.HasValue; } return true; } void IYamlConvertible.Read(IParser parser, Type expectedType, ObjectDeserializer nestedObjectDeserializer) { if (parser.TryConsume(out var @event)) { (Min, Max) = RangeFormatting.ParseIntRange(@event.Value); return; } parser.Consume(); MappingEnd event2; while (!parser.Accept(out event2)) { string text = (parser.Consume().Value ?? "").Trim(); string text2 = text.ToLowerInvariant(); if (!(text2 == "min")) { if (!(text2 == "max")) { throw new YamlException("Unsupported range key '" + text + "'. Only 'min' and 'max' are supported."); } Max = RangeFormatting.ParseNullableInt(parser.Consume().Value); } else { Min = RangeFormatting.ParseNullableInt(parser.Consume().Value); } } parser.Consume(); } void IYamlConvertible.Write(IEmitter emitter, ObjectSerializer nestedObjectSerializer) { emitter.Emit(new YamlDotNet.Core.Events.Scalar(RangeFormatting.FormatShorthand(this))); } } internal sealed class FloatRangeDefinition : IYamlConvertible { public float? Min { get; set; } public float? Max { get; set; } internal bool HasValues() { if (!Min.HasValue) { return Max.HasValue; } return true; } void IYamlConvertible.Read(IParser parser, Type expectedType, ObjectDeserializer nestedObjectDeserializer) { if (parser.TryConsume(out var @event)) { (Min, Max) = RangeFormatting.ParseFloatRange(@event.Value); return; } parser.Consume(); MappingEnd event2; while (!parser.Accept(out event2)) { string text = (parser.Consume().Value ?? "").Trim(); string text2 = text.ToLowerInvariant(); if (!(text2 == "min")) { if (!(text2 == "max")) { throw new YamlException("Unsupported range key '" + text + "'. Only 'min' and 'max' are supported."); } Max = RangeFormatting.ParseNullableFloat(parser.Consume().Value); } else { Min = RangeFormatting.ParseNullableFloat(parser.Consume().Value); } } parser.Consume(); } void IYamlConvertible.Write(IEmitter emitter, ObjectSerializer nestedObjectSerializer) { emitter.Emit(new YamlDotNet.Core.Events.Scalar(RangeFormatting.FormatShorthand(this))); } } internal static class RangeFormatting { internal static IntRangeDefinition? From(int? min, int? max) { if (!min.HasValue && !max.HasValue) { return null; } return new IntRangeDefinition { Min = min, Max = max }; } internal static IntRangeDefinition? FromReference(int actualMin, int actualMax, int defaultMin, int defaultMax) { if (actualMin == defaultMin && actualMax == defaultMax) { return null; } return From(actualMin, actualMax); } internal static FloatRangeDefinition? From(float? min, float? max) { if (!min.HasValue && !max.HasValue) { return null; } return new FloatRangeDefinition { Min = min, Max = max }; } internal static FloatRangeDefinition? FromReference(float actualMin, float actualMax, float defaultMin, float defaultMax) { if (Math.Abs(actualMin - defaultMin) < 0.0001f && Math.Abs(actualMax - defaultMax) < 0.0001f) { return null; } return From(actualMin, actualMax); } internal static int? GetMin(IntRangeDefinition? range, int? fallbackMin) { return range?.Min ?? fallbackMin; } internal static int? GetMax(IntRangeDefinition? range, int? fallbackMin, int? fallbackMax) { return range?.Max ?? range?.Min ?? fallbackMax ?? fallbackMin; } internal static float? GetMin(FloatRangeDefinition? range, float? fallbackMin) { return range?.Min ?? fallbackMin; } internal static float? GetMax(FloatRangeDefinition? range, float? fallbackMin, float? fallbackMax) { return range?.Max ?? range?.Min ?? fallbackMax ?? fallbackMin; } internal static bool NormalizeAscending(ref int? min, ref int? max) { if (min.HasValue && max.HasValue && min.Value > max.Value) { int? num = max; int? num2 = min; min = num; max = num2; return true; } return false; } internal static bool NormalizeAscending(ref float? min, ref float? max) { if (min.HasValue && max.HasValue && min.Value > max.Value) { float? num = max; float? num2 = min; min = num; max = num2; return true; } return false; } internal static string FormatShorthand(IntRangeDefinition? range) { if (range == null || !range.HasValues()) { return ""; } int? min = range.Min; int? max = range.Max; NormalizeDisplayOrder(ref min, ref max); return FormatShorthand(min, max, (int value) => value.ToString(CultureInfo.InvariantCulture)); } internal static string FormatShorthand(FloatRangeDefinition? range) { if (range == null || !range.HasValues()) { return ""; } float? min = range.Min; float? max = range.Max; NormalizeDisplayOrder(ref min, ref max); return FormatShorthand(min, max, (float value) => FormatYamlFloat(value)); } internal static string FormatInlineObject(IntRangeDefinition? range) { int? min = range?.Min; int? max = range?.Max; NormalizeDisplayOrder(ref min, ref max); return FormatInlineObject(min, max, (int value) => value.ToString(CultureInfo.InvariantCulture)); } internal static string FormatInlineObject(FloatRangeDefinition? range) { float? min = range?.Min; float? max = range?.Max; NormalizeDisplayOrder(ref min, ref max); return FormatInlineObject(min, max, (float value) => FormatYamlFloat(value)); } internal static (int? min, int? max) ParseIntRange(string? raw) { string text = (raw ?? "").Trim(); if (text.Length == 0 || text == "~") { return (min: null, max: null); } int num = text.IndexOf('~'); if (num < 0) { int value = ParseRequiredInt(text); return (min: value, max: value); } string raw2 = text.Substring(0, num).Trim(); return new ValueTuple(item2: ParseNullableInt(text.Substring(num + 1).Trim()), item1: ParseNullableInt(raw2)); } internal static (float? min, float? max) ParseFloatRange(string? raw) { string text = (raw ?? "").Trim(); if (text.Length == 0 || text == "~") { return (min: null, max: null); } int num = text.IndexOf('~'); if (num < 0) { float value = ParseRequiredFloat(text); return (min: value, max: value); } string raw2 = text.Substring(0, num).Trim(); return new ValueTuple(item2: ParseNullableFloat(text.Substring(num + 1).Trim()), item1: ParseNullableFloat(raw2)); } internal static int? ParseNullableInt(string? raw) { string text = (raw ?? "").Trim(); if (text.Length != 0) { return ParseRequiredInt(text); } return null; } internal static float? ParseNullableFloat(string? raw) { string text = (raw ?? "").Trim(); if (text.Length != 0) { return ParseRequiredFloat(text); } return null; } private static int ParseRequiredInt(string raw) { if (!int.TryParse(raw, NumberStyles.Integer, CultureInfo.InvariantCulture, out var result)) { throw new YamlException("'" + raw + "' is not a valid integer range value."); } return result; } private static float ParseRequiredFloat(string raw) { if (!float.TryParse(raw, NumberStyles.Float | NumberStyles.AllowThousands, CultureInfo.InvariantCulture, out var result)) { throw new YamlException("'" + raw + "' is not a valid float range value."); } return result; } private static string FormatShorthand(T? min, T? max, Func formatter) where T : struct { if (min.HasValue && max.HasValue && EqualityComparer.Default.Equals(min.Value, max.Value)) { return formatter(min.Value); } return (min.HasValue ? formatter(min.Value) : "") + "~" + (max.HasValue ? formatter(max.Value) : ""); } private static string FormatInlineObject(T? min, T? max, Func formatter) where T : struct { return "{ min: " + (min.HasValue ? formatter(min.Value) : "") + ", max: " + (max.HasValue ? formatter(max.Value) : "") + " }"; } private static void NormalizeDisplayOrder(ref int? min, ref int? max) { NormalizeAscending(ref min, ref max); } private static void NormalizeDisplayOrder(ref float? min, ref float? max) { NormalizeAscending(ref min, ref max); } private static string FormatYamlFloat(float value) { return value.ToString("0.###", CultureInfo.InvariantCulture); } } internal sealed class OfferingBowlSnapshot { public string BossItem { get; set; } = ""; public int BossItems { get; set; } public string BossPrefab { get; set; } = ""; public string ItemPrefab { get; set; } = ""; public string SetGlobalKey { get; set; } = ""; public bool RenderSpawnAreaGizmos { get; set; } public bool AlertOnSpawn { get; set; } public float SpawnBossDelay { get; set; } public float SpawnBossMaxDistance { get; set; } public float SpawnBossMinDistance { get; set; } public float SpawnBossMaxYDistance { get; set; } public int GetSolidHeightMargin { get; set; } public bool EnableSolidHeightCheck { get; set; } public float SpawnPointClearingRadius { get; set; } public float SpawnYOffset { get; set; } public bool UseItemStands { get; set; } public string ItemStandPrefix { get; set; } = ""; public float ItemStandMaxRange { get; set; } } internal sealed class ItemStandSnapshot { public bool CanBeRemoved { get; set; } public bool AutoAttach { get; set; } public string OrientationType { get; set; } = ""; public List SupportedTypes { get; set; } = new List(); public List SupportedItems { get; set; } = new List(); public List UnsupportedItems { get; set; } = new List(); public float PowerActivationDelay { get; set; } public string GuardianPower { get; set; } = ""; } internal sealed class OfferingBowlRuntimeState : MonoBehaviour { public OfferingBowlSnapshot? Snapshot { get; set; } public bool Applied { get; set; } public float RespawnMinutes { get; set; } public long LocalLastUseTicks { get; set; } public string? PendingRefundPayload { get; set; } } internal sealed class ItemStandRuntimeState : MonoBehaviour { public ItemStandSnapshot? Snapshot { get; set; } public bool Applied { get; set; } } internal sealed class BossStoneItemStandRuntimeState : MonoBehaviour { public bool Resolved { get; set; } public bool ShouldHandle { get; set; } public string GuardianPowerName { get; set; } = ""; public string PlayerKey { get; set; } = ""; } internal static class SceneProximityQueries { internal static bool TryFindAnyLivingPlayerInRangeXZ(Vector3 point, float range, out long playerId) { //IL_0019: 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_006b: Unknown result type (might be due to invalid IL or missing references) playerId = 0L; if (range <= 0f) { return false; } float rangeSquared = range * range; if (BossRulesPlugin.IsRuntimeServer()) { return TryFindAnyServerPlayerInRangeXZ(point, rangeSquared, out playerId); } foreach (Player allPlayer in Player.GetAllPlayers()) { if (!((Object)(object)allPlayer == (Object)null) && !((Object)(object)((Component)allPlayer).gameObject == (Object)null) && !((Character)allPlayer).IsDead()) { long playerID = allPlayer.GetPlayerID(); if (playerID != 0L && IsWithinRangeXZ(((Component)allPlayer).transform.position, point, rangeSquared)) { playerId = playerID; return true; } } } return false; } internal static bool TryFindNearestLivingPlayerInRangeXZ(Vector3 point, float range, out long playerId) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_006e: 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_0074: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) playerId = 0L; if (range <= 0f) { return false; } float num = range * range; if (BossRulesPlugin.IsRuntimeServer()) { return TryFindNearestServerPlayerInRangeXZ(point, num, out playerId); } float num2 = float.MaxValue; foreach (Player allPlayer in Player.GetAllPlayers()) { if ((Object)(object)allPlayer == (Object)null || (Object)(object)((Component)allPlayer).gameObject == (Object)null || ((Character)allPlayer).IsDead()) { continue; } long playerID = allPlayer.GetPlayerID(); if (playerID != 0L) { Vector3 val = ((Component)allPlayer).transform.position - point; val.y = 0f; float sqrMagnitude = ((Vector3)(ref val)).sqrMagnitude; if (!(sqrMagnitude >= num) && !(sqrMagnitude >= num2)) { num2 = sqrMagnitude; playerId = playerID; } } } return playerId != 0; } private static bool TryFindAnyServerPlayerInRangeXZ(Vector3 point, float rangeSquared, out long playerId) { //IL_000d: 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) playerId = 0L; long uID = ZNet.GetUID(); if (uID != 0L && IsLocalServerPlayerInRangeXZ(point, rangeSquared, livingPlayersOnly: true)) { playerId = uID; return true; } ZNet instance = ZNet.instance; List list = ((instance != null) ? instance.GetPeers() : null); if (list == null) { return false; } foreach (ZNetPeer item in list) { if (item != null && item.m_uid != 0L && IsServerPeerInRangeXZ(item, point, rangeSquared, livingPlayersOnly: true)) { playerId = item.m_uid; return true; } } return false; } private static bool TryFindNearestServerPlayerInRangeXZ(Vector3 point, float rangeSquared, out long playerId) { //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_0044: 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_00a9: 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) //IL_00c0: 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) playerId = 0L; float num = float.MaxValue; Player localPlayer = Player.m_localPlayer; long uID = ZNet.GetUID(); if (uID != 0L && (Object)(object)localPlayer != (Object)null && (Object)(object)((Component)localPlayer).gameObject != (Object)null && !((Character)localPlayer).IsDead()) { Vector3 val = ((Component)localPlayer).transform.position - point; val.y = 0f; float sqrMagnitude = ((Vector3)(ref val)).sqrMagnitude; if (sqrMagnitude < rangeSquared) { num = sqrMagnitude; playerId = uID; } } ZNet instance = ZNet.instance; List list = ((instance != null) ? instance.GetPeers() : null); if (list == null) { return playerId != 0; } foreach (ZNetPeer item in list) { if (item != null && item.m_uid != 0L && IsServerPeerInRangeXZ(item, point, rangeSquared, livingPlayersOnly: true) && TryGetServerPeerPosition(item, out var position)) { Vector3 val2 = position - point; val2.y = 0f; float sqrMagnitude2 = ((Vector3)(ref val2)).sqrMagnitude; if (!(sqrMagnitude2 >= num)) { num = sqrMagnitude2; playerId = item.m_uid; } } } return playerId != 0; } private static bool IsLocalServerPlayerInRangeXZ(Vector3 point, float rangeSquared, bool livingPlayersOnly) { //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer != (Object)null && (Object)(object)((Component)localPlayer).gameObject != (Object)null && (!livingPlayersOnly || !((Character)localPlayer).IsDead())) { return IsWithinRangeXZ(((Component)localPlayer).transform.position, point, rangeSquared); } return false; } private static bool IsServerPeerInRangeXZ(ZNetPeer? peer, Vector3 point, float rangeSquared, bool livingPlayersOnly) { //IL_0015: 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 (peer == null || !peer.IsReady() || !TryGetServerPeerPosition(peer, out var position) || !IsWithinRangeXZ(position, point, rangeSquared)) { return false; } if (!livingPlayersOnly) { return true; } if (TryGetLoadedPeerPlayer(peer, out Player player)) { if ((Object)(object)player != (Object)null) { return !((Character)player).IsDead(); } return false; } return true; } private static bool TryGetServerPeerPosition(ZNetPeer peer, out Vector3 position) { //IL_0001: 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_007d: 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_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_006a: 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) position = default(Vector3); if (peer == null) { return false; } if (TryGetLoadedPeerPlayer(peer, out Player player) && (Object)(object)player != (Object)null && (Object)(object)((Component)player).gameObject != (Object)null) { position = ((Component)player).transform.position; return true; } if (!((ZDOID)(ref peer.m_characterID)).IsNone() && ZDOMan.instance != null) { ZDO zDO = ZDOMan.instance.GetZDO(peer.m_characterID); if (zDO != null) { position = zDO.GetPosition(); return true; } } position = peer.GetRefPos(); return true; } private static bool TryGetLoadedPeerPlayer(ZNetPeer peer, out Player? player) { //IL_0028: Unknown result type (might be due to invalid IL or missing references) player = null; if (peer == null || ((ZDOID)(ref peer.m_characterID)).IsNone() || (Object)(object)ZNetScene.instance == (Object)null) { return false; } GameObject val = ZNetScene.instance.FindInstance(peer.m_characterID); if ((Object)(object)val != (Object)null) { return val.TryGetComponent(ref player); } return false; } private static bool IsWithinRangeXZ(Vector3 source, Vector3 target, float rangeSquared) { //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_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) Vector3 val = source - target; val.y = 0f; return ((Vector3)(ref val)).sqrMagnitude < rangeSquared; } } } 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; } } }