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.Reflection.Emit; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Serialization; using System.Runtime.Versioning; using System.Security; using System.Security.Cryptography; using System.Security.Permissions; using System.Text; using System.Text.RegularExpressions; using System.Threading; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using Data; using ExpandWorldData; using HarmonyLib; using JetBrains.Annotations; using Microsoft.CodeAnalysis; using ServerSync; using TMPro; using UnityEngine; using UnityEngine.SceneManagement; using YamlDotNet.Core; using YamlDotNet.Core.Events; using YamlDotNet.Core.ObjectPool; using YamlDotNet.Core.Tokens; using YamlDotNet.Helpers; using YamlDotNet.RepresentationModel; using YamlDotNet.Serialization; using YamlDotNet.Serialization.BufferedDeserialization; using YamlDotNet.Serialization.BufferedDeserialization.TypeDiscriminators; using YamlDotNet.Serialization.Callbacks; using YamlDotNet.Serialization.Converters; using YamlDotNet.Serialization.EventEmitters; using YamlDotNet.Serialization.NamingConventions; using YamlDotNet.Serialization.NodeDeserializers; using YamlDotNet.Serialization.NodeTypeResolvers; using YamlDotNet.Serialization.ObjectFactories; using YamlDotNet.Serialization.ObjectGraphTraversalStrategies; using YamlDotNet.Serialization.ObjectGraphVisitors; using YamlDotNet.Serialization.Schemas; using YamlDotNet.Serialization.TypeInspectors; using YamlDotNet.Serialization.TypeResolvers; using YamlDotNet.Serialization.Utilities; using YamlDotNet.Serialization.ValueDeserializers; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyTitle("DropNSpawn")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("sighsorry")] [assembly: AssemblyProduct("DropNSpawn")] [assembly: AssemblyCopyright("Copyright © 2021")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("4358610B-F3F4-4843-B7AF-98B7BC60DCDE")] [assembly: AssemblyFileVersion("1.3.6")] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.3.6.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; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class ExtensionMarkerAttribute : Attribute { private readonly string k__BackingField; public string Name => k__BackingField; public ExtensionMarkerAttribute(string name) { k__BackingField = name; } } } namespace LocalizationManager { [PublicAPI] public class Localizer { private static readonly Dictionary>> PlaceholderProcessors; private static readonly Dictionary> loadedTexts; private static readonly ConditionalWeakTable localizationLanguage; private static readonly List> localizationObjects; private static BaseUnityPlugin? _plugin; private static readonly List fileExtensions; private static BaseUnityPlugin plugin { get { //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Expected O, but got Unknown if (_plugin == null) { IEnumerable source; try { source = Assembly.GetExecutingAssembly().DefinedTypes.ToList(); } catch (ReflectionTypeLoadException ex) { source = from t in ex.Types where t != null select t.GetTypeInfo(); } _plugin = (BaseUnityPlugin)Chainloader.ManagerObject.GetComponent((Type)source.First((TypeInfo t) => t.IsClass && typeof(BaseUnityPlugin).IsAssignableFrom(t))); } return _plugin; } } public static event Action? OnLocalizationComplete; private static void UpdatePlaceholderText(Localization localization, string key) { localizationLanguage.TryGetValue(localization, out string value); string text = loadedTexts[value][key]; if (PlaceholderProcessors.TryGetValue(key, out Dictionary> value2)) { text = value2.Aggregate(text, (string current, KeyValuePair> kv) => current.Replace("{" + kv.Key + "}", kv.Value())); } localization.AddWord(key, text); } public static void AddPlaceholder(string key, string placeholder, ConfigEntry config, Func? convertConfigValue = null) where T : notnull { if (convertConfigValue == null) { convertConfigValue = (T val) => val.ToString(); } if (!PlaceholderProcessors.ContainsKey(key)) { PlaceholderProcessors[key] = new Dictionary>(); } config.SettingChanged += delegate { UpdatePlaceholder(); }; if (loadedTexts.ContainsKey(Localization.instance.GetSelectedLanguage())) { UpdatePlaceholder(); } void UpdatePlaceholder() { PlaceholderProcessors[key][placeholder] = () => convertConfigValue(config.Value); UpdatePlaceholderText(Localization.instance, key); } } public static void AddText(string key, string text) { List> list = new List>(); foreach (WeakReference localizationObject in localizationObjects) { if (localizationObject.TryGetTarget(out var target)) { Dictionary dictionary = loadedTexts[localizationLanguage.GetOrCreateValue(target)]; if (!target.m_translations.ContainsKey(key)) { dictionary[key] = text; target.AddWord(key, text); } } else { list.Add(localizationObject); } } foreach (WeakReference item in list) { localizationObjects.Remove(item); } } public static void Load() { _ = plugin; } public static void LoadLocalizationLater(Localization __instance) { LoadLocalization(Localization.instance, __instance.GetSelectedLanguage()); } public static void SafeCallLocalizeComplete() { Localizer.OnLocalizationComplete?.Invoke(); } private static void LoadLocalization(Localization __instance, string language) { if (!localizationLanguage.Remove(__instance)) { localizationObjects.Add(new WeakReference(__instance)); } localizationLanguage.Add(__instance, language); Dictionary dictionary = new Dictionary(); foreach (string item in from f in Directory.GetFiles(Path.GetDirectoryName(Paths.PluginPath), plugin.Info.Metadata.Name + ".*", SearchOption.AllDirectories) where fileExtensions.IndexOf(Path.GetExtension(f)) >= 0 select f) { string[] array = Path.GetFileNameWithoutExtension(item).Split('.'); if (array.Length >= 2) { string text = array[1]; if (dictionary.ContainsKey(text)) { Debug.LogWarning((object)("Duplicate key " + text + " found for " + plugin.Info.Metadata.Name + ". The duplicate file found at " + item + " will be skipped.")); } else { dictionary[text] = item; } } } byte[] array2 = LoadTranslationFromAssembly("English"); if (array2 == null) { throw new Exception("Found no English localizations in mod " + plugin.Info.Metadata.Name + ". Expected an embedded resource translations/English.json or translations/English.yml."); } Dictionary dictionary2 = new DeserializerBuilder().IgnoreFields().Build().Deserialize>(Encoding.UTF8.GetString(array2)); if (dictionary2 == null) { throw new Exception("Localization for mod " + plugin.Info.Metadata.Name + " failed: Localization file was empty."); } string text2 = null; if (language != "English") { if (dictionary.TryGetValue(language, out var value)) { text2 = File.ReadAllText(value); } else { byte[] array3 = LoadTranslationFromAssembly(language); if (array3 != null) { text2 = Encoding.UTF8.GetString(array3); } } } if (text2 == null && dictionary.TryGetValue("English", out var value2)) { text2 = File.ReadAllText(value2); } if (text2 != null) { foreach (KeyValuePair item2 in new DeserializerBuilder().IgnoreFields().Build().Deserialize>(text2) ?? new Dictionary()) { dictionary2[item2.Key] = item2.Value; } } loadedTexts[language] = dictionary2; foreach (KeyValuePair item3 in dictionary2) { UpdatePlaceholderText(__instance, item3.Key); } } static Localizer() { //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Expected O, but got Unknown //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_00ca: Expected O, but got Unknown //IL_00f8: Unknown result type (might be due to invalid IL or missing references) //IL_0105: Expected O, but got Unknown PlaceholderProcessors = new Dictionary>>(); loadedTexts = new Dictionary>(); localizationLanguage = new ConditionalWeakTable(); localizationObjects = new List>(); fileExtensions = new List(2) { ".json", ".yml" }; Harmony val = new Harmony("org.bepinex.helpers.LocalizationManager"); val.Patch((MethodBase)AccessTools.DeclaredMethod(typeof(Localization), "SetupLanguage", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(AccessTools.DeclaredMethod(typeof(Localizer), "LoadLocalization", (Type[])null, (Type[])null)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); val.Patch((MethodBase)AccessTools.DeclaredMethod(typeof(FejdStartup), "SetupGui", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(AccessTools.DeclaredMethod(typeof(Localizer), "LoadLocalizationLater", (Type[])null, (Type[])null)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); val.Patch((MethodBase)AccessTools.DeclaredMethod(typeof(FejdStartup), "Start", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(AccessTools.DeclaredMethod(typeof(Localizer), "SafeCallLocalizeComplete", (Type[])null, (Type[])null)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } private static byte[]? LoadTranslationFromAssembly(string language) { foreach (string fileExtension in fileExtensions) { byte[] array = ReadEmbeddedFileBytes("translations." + language + fileExtension); if (array != null) { return array; } } return null; } public static byte[]? ReadEmbeddedFileBytes(string resourceFileName, Assembly? containingAssembly = null) { using MemoryStream memoryStream = new MemoryStream(); if ((object)containingAssembly == null) { containingAssembly = Assembly.GetCallingAssembly(); } string text = containingAssembly.GetManifestResourceNames().FirstOrDefault((string str) => str.EndsWith(resourceFileName, StringComparison.Ordinal)); if (text != null) { containingAssembly.GetManifestResourceStream(text)?.CopyTo(memoryStream); } return (memoryStream.Length == 0L) ? null : memoryStream.ToArray(); } } public static class LocalizationManagerVersion { public const string Version = "1.4.1"; } } namespace DropNSpawn { [HarmonyPatch(typeof(ZNetScene), "Awake")] internal static class ZNetSceneAwakePatch { private static void Postfix() { DropNSpawnPlugin.QueueGameDataRefresh(DropNSpawnPlugin.ReloadDomain.All, "ZNetScene.Awake"); } } [HarmonyPatch(typeof(ObjectDB), "Awake")] internal static class ObjectDBAwakePatch { private static void Postfix() { DropNSpawnPlugin.QueueGameDataRefresh(DropNSpawnPlugin.ReloadDomain.All, "ObjectDB.Awake"); } } [HarmonyPatch(typeof(ObjectDB), "CopyOtherDB")] internal static class ObjectDBCopyOtherDBPatch { private static void Postfix() { DropNSpawnPlugin.QueueGameDataRefresh(DropNSpawnPlugin.ReloadDomain.All, "ObjectDB.CopyOtherDB"); } } [HarmonyPatch(typeof(ZNet), "Shutdown")] internal static class ZNetShutdownPatch { private static void Prefix() { CharacterDropManager.ResetWorldRuntimeState(); } } [HarmonyPatch(typeof(ZoneSystem), "Start")] internal static class ZoneSystemStartPatch { private static void Postfix() { DropNSpawnPlugin.QueueGameDataRefresh(DropNSpawnPlugin.ReloadDomain.SpawnSystem, "ZoneSystem.Start"); } } [HarmonyPatch(typeof(RandEventSystem), "Awake")] internal static class RandEventSystemAwakePatch { private static void Postfix() { EventManager.ApplyGlobalEventSettings(); } } [HarmonyPatch(typeof(RandEventSystem), "Start")] internal static class RandEventSystemStartPatch { private static void Postfix() { EventManager.ApplyGlobalEventSettings(); } } [HarmonyPatch(typeof(ZoneSystem), "Start")] internal static class ZoneSystemStartEventPatch { [HarmonyPriority(0)] private static void Postfix() { EventManager.NotifyGameDataReady("ZoneSystem.Start"); } } [HarmonyPatch(typeof(RandEventSystem), "OnDestroy")] internal static class RandEventSystemOnDestroyPatch { private static void Prefix() { EventManager.OnRandEventSystemDestroyed(); } } [HarmonyPatch(typeof(RandEventSystem), "FixedUpdate")] internal static class RandEventSystemFixedUpdatePatch { private static bool Prefix(RandEventSystem __instance) { return !EventManager.TryRunMultipleEventsFixedUpdate(__instance); } } [HarmonyPatch(typeof(RandEventSystem), "SetRandomEvent")] internal static class RandEventSystemSetRandomEventPatch { private static bool Prefix(RandEventSystem __instance, RandomEvent ev, Vector3 pos) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) return !EventManager.TryHandleMultipleSetRandomEvent(__instance, ev, pos); } } [HarmonyPatch(typeof(RandEventSystem), "SendCurrentRandomEvent")] internal static class RandEventSystemSendCurrentRandomEventPatch { private static bool Prefix(RandEventSystem __instance) { return !EventManager.TrySendMultipleCurrentRandomEvent(__instance); } } [HarmonyPatch(typeof(RandEventSystem), "UpdateRandomEvent")] internal static class RandEventSystemUpdateRandomEventPatch { private static bool Prefix(RandEventSystem __instance, float dt) { return !EventManager.TryRunCheckPerPlayerRandomUpdate(__instance, dt); } } [HarmonyPatch(typeof(RandomEvent), "Clone")] internal static class RandomEventClonePatch { private static void Postfix(RandomEvent __instance, RandomEvent __result) { EventManager.CopySpawnPayloads(__instance, __result); } } [HarmonyPatch(typeof(RandEventSystem), "CheckBase")] internal static class RandEventSystemCheckBasePatch { private static bool Prefix(RandomEvent ev, PlayerEventData player, ref bool __result) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) if (!EventManager.TryCheckBase(ev, player, out var result)) { return true; } __result = result; return false; } } [HarmonyPatch(typeof(RandEventSystem), "InValidBiome")] internal static class RandEventSystemInValidBiomePatch { private static void Postfix(RandomEvent ev, Vector3 point, ref bool __result) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) __result = EventManager.PassesExtraChecks(ev, point, __result); } } [HarmonyPatch(typeof(RandomEvent), "OnStart")] internal static class RandomEventOnStartPatch { private static void Postfix(RandomEvent __instance) { EventManager.RunStartCommands(__instance); } } [HarmonyPatch(typeof(RandomEvent), "OnStop")] internal static class RandomEventOnStopPatch { private static void Postfix(RandomEvent __instance) { EventManager.RunEndCommands(__instance); } } [HarmonyPatch(typeof(Location), "OnDestroy")] internal static class LocationOnDestroyPatch { private static void Prefix(Location __instance) { SpawnerManager.UntrackLocationInstanceProvenance(__instance); } } [HarmonyPatch(typeof(LocationProxy), "SpawnLocation")] internal static class LocationProxySpawnLocationPatch { private static readonly FieldRef InstanceRef = AccessTools.FieldRefAccess("m_instance"); private static void Postfix(LocationProxy __instance, bool __result) { if (__result && PluginSettingsFacade.IsSpawnerDomainEnabled() && !DropNSpawnPlugin.IsGameDataRefreshDeferred(DropNSpawnPlugin.ReloadDomain.Spawner)) { GameObject spawnedRoot = (((Object)(object)__instance != (Object)null) ? InstanceRef.Invoke(__instance) : null); SpawnerManager.RecordSpawnedLocationProxyProvenance(__instance, spawnedRoot); } } } internal readonly struct TemporaryFieldOverrideState where T : class { internal bool Applied { get; } internal T? Previous { get; } internal TemporaryFieldOverrideState(T? previous) { Applied = true; Previous = previous; } } [HarmonyPatch(typeof(Piece), "Awake")] internal static class PieceAwakePatch { private static void Postfix(Piece __instance) { ObjectDropManager.QueueObjectInstanceReconcile(((Component)__instance).gameObject, ObjectDropManager.LiveObjectComponentKind.Piece); } } [HarmonyPatch(typeof(Piece), "SetCreator")] internal static class PieceSetCreatorPatch { private static void Postfix(Piece __instance) { if (PluginSettingsFacade.IsObjectDomainEnabled()) { ObjectDropManager.ReconcilePieceInstance(__instance); } } } [HarmonyPatch(typeof(Piece), "OnDestroy")] internal static class PieceOnDestroyPatch { private static void Prefix(Piece __instance) { ObjectDropManager.UntrackObjectInstance(((Component)__instance).gameObject); } } [HarmonyPatch(typeof(ZNetView), "OnDestroy")] internal static class ZNetViewOnDestroyPatch { private static void Prefix(ZNetView __instance) { ObjectDropManager.UntrackObjectInstance(((Component)__instance).gameObject); SpawnArea component = ((Component)__instance).GetComponent(); if ((Object)(object)component != (Object)null) { SpawnerManager.UntrackSpawnAreaInstance(component); } } } [HarmonyPatch(typeof(Destructible), "Awake")] internal static class DestructibleAwakePatch { private static void Postfix(Destructible __instance) { ObjectDropManager.QueueObjectInstanceReconcile(((Component)__instance).gameObject, ObjectDropManager.LiveObjectComponentKind.Destructible); } } [HarmonyPatch(typeof(Destructible), "GetDestructibleType")] internal static class DestructibleGetDestructibleTypePatch { private static void Prefix(Destructible __instance) { if (PluginSettingsFacade.IsObjectDomainEnabled()) { ObjectDropManager.ApplyLazyDestructibleTypeIfNeeded(__instance); } } } [HarmonyPatch(typeof(Destructible), "RPC_Damage")] internal static class DestructibleDamagePatch { private static void Prefix(Destructible __instance) { if (PluginSettingsFacade.IsObjectDomainEnabled()) { ObjectDropManager.ApplyLazyDestructibleScalarsIfNeeded(__instance); } } } [HarmonyPatch(typeof(Destructible), "Destroy")] internal static class DestructibleDestroyPatch { private static void Prefix(Destructible __instance, out TemporaryFieldOverrideState __state) { __state = default(TemporaryFieldOverrideState); if (PluginSettingsFacade.IsObjectDomainEnabled() && ObjectDropManager.TryOverrideConditionalDestructibleSpawnWhenDestroyed(__instance, out GameObject previous)) { __state = new TemporaryFieldOverrideState(previous); } } private static void Postfix(Destructible __instance, TemporaryFieldOverrideState __state) { if (__state.Applied) { __instance.m_spawnWhenDestroyed = __state.Previous; } } private static Exception? Finalizer(Destructible __instance, TemporaryFieldOverrideState __state, Exception? __exception) { if (__exception != null && __state.Applied) { __instance.m_spawnWhenDestroyed = __state.Previous; } return __exception; } } [HarmonyPatch(typeof(DropOnDestroyed), "Awake")] internal static class DropOnDestroyedAwakePatch { private static void Postfix(DropOnDestroyed __instance) { ObjectDropManager.QueueObjectInstanceReconcile(((Component)__instance).gameObject, ObjectDropManager.LiveObjectComponentKind.DropOnDestroyed); } } [HarmonyPatch(typeof(Container), "Awake")] internal static class ContainerAwakePatch { private static void Postfix(Container __instance) { ObjectDropManager.QueueObjectInstanceReconcile(((Component)__instance).gameObject, ObjectDropManager.LiveObjectComponentKind.Container); } } [HarmonyPatch(typeof(Pickable), "Awake")] internal static class PickableAwakePatch { private static void Postfix(Pickable __instance) { if (!PluginSettingsFacade.IsObjectDomainEnabled()) { ObjectDropManager.TrackObjectInstance(((Component)__instance).gameObject); } else { ObjectDropManager.QueueObjectInstanceReconcile(((Component)__instance).gameObject, ObjectDropManager.LiveObjectComponentKind.Pickable); } } } [HarmonyPatch(typeof(PickableItem), "Awake")] internal static class PickableItemAwakePatch { private static void Postfix(PickableItem __instance) { if (!PluginSettingsFacade.IsObjectDomainEnabled()) { ObjectDropManager.TrackObjectInstance(((Component)__instance).gameObject); } else { ObjectDropManager.QueueObjectInstanceReconcile(((Component)__instance).gameObject, ObjectDropManager.LiveObjectComponentKind.PickableItem); } } } [HarmonyPatch(typeof(Fish), "Awake")] internal static class FishAwakePatch { private static void Postfix(Fish __instance) { ObjectDropManager.QueueObjectInstanceReconcile(((Component)__instance).gameObject, ObjectDropManager.LiveObjectComponentKind.Fish); } } [HarmonyPatch(typeof(Container), "AddDefaultItems")] internal static class ContainerAddDefaultItemsPatch { private static void Prefix(Container __instance, out TemporaryFieldOverrideState __state) { __state = default(TemporaryFieldOverrideState); if (PluginSettingsFacade.IsObjectDomainEnabled() && ObjectDropManager.TryOverrideContainerDrops(__instance, out DropTable previous)) { __state = new TemporaryFieldOverrideState(previous); } } private static void Postfix(Container __instance, TemporaryFieldOverrideState __state) { if (__state.Applied) { __instance.m_defaultItems = __state.Previous; } } private static Exception? Finalizer(Container __instance, TemporaryFieldOverrideState __state, Exception? __exception) { if (__exception != null && __state.Applied) { __instance.m_defaultItems = __state.Previous; } return __exception; } } [HarmonyPatch(typeof(DropOnDestroyed), "OnDestroyed")] internal static class DropOnDestroyedPatch { private static void Prefix(DropOnDestroyed __instance, out TemporaryFieldOverrideState __state) { __state = default(TemporaryFieldOverrideState); if (PluginSettingsFacade.IsObjectDomainEnabled() && ObjectDropManager.TryOverrideConditionalDropOnDestroyed(__instance, out DropTable previous)) { __state = new TemporaryFieldOverrideState(previous); } } private static void Postfix(DropOnDestroyed __instance, TemporaryFieldOverrideState __state) { if (__state.Applied) { __instance.m_dropWhenDestroyed = __state.Previous; } } private static Exception? Finalizer(DropOnDestroyed __instance, TemporaryFieldOverrideState __state, Exception? __exception) { if (__exception != null && __state.Applied) { __instance.m_dropWhenDestroyed = __state.Previous; } return __exception; } } [HarmonyPatch(typeof(MineRock), "RPC_Hit")] internal static class MineRockHitPatch { private static void Prefix(MineRock __instance, out TemporaryFieldOverrideState __state) { __state = default(TemporaryFieldOverrideState); if (PluginSettingsFacade.IsObjectDomainEnabled()) { ObjectDropManager.ApplyLazyMineRockScalarsIfNeeded(__instance); if (ObjectDropManager.TryOverrideConditionalMineRockDrops(__instance, out DropTable previous)) { __state = new TemporaryFieldOverrideState(previous); } } } private static void Postfix(MineRock __instance, TemporaryFieldOverrideState __state) { if (__state.Applied) { __instance.m_dropItems = __state.Previous; } } private static Exception? Finalizer(MineRock __instance, TemporaryFieldOverrideState __state, Exception? __exception) { if (__exception != null && __state.Applied) { __instance.m_dropItems = __state.Previous; } return __exception; } } [HarmonyPatch(typeof(MineRock), "Start")] internal static class MineRockStartPatch { private static void Postfix(MineRock __instance) { ObjectDropManager.QueueObjectInstanceReconcile(((Component)__instance).gameObject, ObjectDropManager.LiveObjectComponentKind.MineRock); } } [HarmonyPatch(typeof(MineRock5), "RPC_Damage")] internal static class MineRock5DamagePatch { private static void Prefix(MineRock5 __instance, out TemporaryFieldOverrideState __state) { __state = default(TemporaryFieldOverrideState); if (PluginSettingsFacade.IsObjectDomainEnabled()) { ObjectDropManager.ApplyLazyMineRock5ScalarsIfNeeded(__instance); if (ObjectDropManager.TryOverrideConditionalMineRock5Drops(__instance, out DropTable previous)) { __state = new TemporaryFieldOverrideState(previous); } } } private static void Postfix(MineRock5 __instance, TemporaryFieldOverrideState __state) { if (__state.Applied) { __instance.m_dropItems = __state.Previous; } } private static Exception? Finalizer(MineRock5 __instance, TemporaryFieldOverrideState __state, Exception? __exception) { if (__exception != null && __state.Applied) { __instance.m_dropItems = __state.Previous; } return __exception; } } [HarmonyPatch(typeof(MineRock5), "Awake")] internal static class MineRock5AwakePatch { private static void Postfix(MineRock5 __instance) { ObjectDropManager.QueueObjectInstanceReconcile(((Component)__instance).gameObject, ObjectDropManager.LiveObjectComponentKind.MineRock5); } } [HarmonyPatch(typeof(TreeBase), "RPC_Damage")] internal static class TreeBaseDamagePatch { private static void Prefix(TreeBase __instance, out TemporaryFieldOverrideState __state) { __state = default(TemporaryFieldOverrideState); if (PluginSettingsFacade.IsObjectDomainEnabled()) { ObjectDropManager.ApplyLazyTreeBaseScalarsIfNeeded(__instance); if (ObjectDropManager.TryOverrideConditionalTreeBaseDrops(__instance, out DropTable previous)) { __state = new TemporaryFieldOverrideState(previous); } } } private static void Postfix(TreeBase __instance, TemporaryFieldOverrideState __state) { if (__state.Applied) { __instance.m_dropWhenDestroyed = __state.Previous; } } private static Exception? Finalizer(TreeBase __instance, TemporaryFieldOverrideState __state, Exception? __exception) { if (__exception != null && __state.Applied) { __instance.m_dropWhenDestroyed = __state.Previous; } return __exception; } } [HarmonyPatch(typeof(TreeBase), "Awake")] internal static class TreeBaseAwakePatch { private static void Postfix(TreeBase __instance) { ObjectDropManager.QueueObjectInstanceReconcile(((Component)__instance).gameObject, ObjectDropManager.LiveObjectComponentKind.TreeBase); } } [HarmonyPatch(typeof(TreeLog), "Destroy")] internal static class TreeLogDestroyPatch { private static void Prefix(TreeLog __instance, out TemporaryFieldOverrideState __state) { __state = default(TemporaryFieldOverrideState); if (PluginSettingsFacade.IsObjectDomainEnabled() && ObjectDropManager.TryOverrideConditionalTreeLogDrops(__instance, out DropTable previous)) { __state = new TemporaryFieldOverrideState(previous); } } private static void Postfix(TreeLog __instance, TemporaryFieldOverrideState __state) { if (__state.Applied) { __instance.m_dropWhenDestroyed = __state.Previous; } } private static Exception? Finalizer(TreeLog __instance, TemporaryFieldOverrideState __state, Exception? __exception) { if (__exception != null && __state.Applied) { __instance.m_dropWhenDestroyed = __state.Previous; } return __exception; } } [HarmonyPatch(typeof(TreeLog), "RPC_Damage")] internal static class TreeLogDamagePatch { private static void Prefix(TreeLog __instance) { if (PluginSettingsFacade.IsObjectDomainEnabled()) { ObjectDropManager.ApplyLazyTreeLogScalarsIfNeeded(__instance); } } } [HarmonyPatch(typeof(TreeLog), "Awake")] internal static class TreeLogAwakePatch { private static void Postfix(TreeLog __instance) { ObjectDropManager.QueueObjectInstanceReconcile(((Component)__instance).gameObject, ObjectDropManager.LiveObjectComponentKind.TreeLog); } } [HarmonyPatch(typeof(CharacterDrop), "GenerateDropList")] internal static class CharacterDropGenerateDropListPatch { private readonly struct State { internal List? PreviousDrops { get; } internal bool HasOnePerPlayerScope { get; } internal IReadOnlyList? AmountSourceDrops { get; } internal State(List? previousDrops, bool hasOnePerPlayerScope, IReadOnlyList? amountSourceDrops) { PreviousDrops = previousDrops; HasOnePerPlayerScope = hasOnePerPlayerScope; AmountSourceDrops = amountSourceDrops; } } private static void Prefix(CharacterDrop __instance, out State __state) { __state = new State(null, hasOnePerPlayerScope: false, null); bool flag = PluginSettingsFacade.IsCharacterDomainEnabled(); if (flag || CharacterDropManager.IsGlobalCharacterLootLevelScalingEnabled()) { List list = (flag ? CharacterDropManager.OverrideConditionalDrops(__instance) : null); __state = new State(list, hasOnePerPlayerScope: false, null); IReadOnlyList sourceDrops; List list2 = CharacterDropManager.SuppressGlobalCharacterLootLevelMultiplierDrops(__instance, out sourceDrops); if (list == null) { list = list2; } __state = new State(list, hasOnePerPlayerScope: false, sourceDrops); bool hasOnePerPlayerScope = flag && CharacterDropManager.BeginOnePerPlayerNearbyPlayerScope(__instance); __state = new State(list, hasOnePerPlayerScope, sourceDrops); } } [HarmonyPriority(0)] private static void Postfix(CharacterDrop __instance, List> __result, State __state) { CharacterDropManager.ApplyGlobalCharacterLootLevelMultiplier(__instance, __result, __state.AmountSourceDrops); if (__state.PreviousDrops != null) { __instance.m_drops = __state.PreviousDrops; } } private static Exception? Finalizer(CharacterDrop __instance, State __state, Exception? __exception) { if (__state.PreviousDrops != null) { __instance.m_drops = __state.PreviousDrops; } if (__state.HasOnePerPlayerScope) { CharacterDropManager.EndOnePerPlayerNearbyPlayerScope(); } return __exception; } } [HarmonyPatch(typeof(ZNet), "GetNrOfPlayers")] internal static class ZNetGetNrOfPlayersPatch { private static bool Prefix(ref int __result) { if (!PluginSettingsFacade.IsCharacterDomainEnabled()) { return true; } if (!CharacterDropManager.TryGetScopedOnePerPlayerNearbyPlayerCount(out var playerCount)) { return true; } __result = playerCount; return false; } } [HarmonyPatch(typeof(CharacterDrop), "Start")] internal static class CharacterDropStartPatch { private static void Postfix(CharacterDrop __instance) { if (PluginSettingsFacade.IsCharacterDomainEnabled()) { CharacterDropManager.TrackCharacterDropInstance(__instance); } } } [HarmonyPatch(typeof(Character), "OnDestroy")] internal static class CharacterOnDestroyCharacterDropPatch { private static void Postfix(Character __instance) { CharacterDrop characterDrop = default(CharacterDrop); if ((Object)(object)__instance != (Object)null && ((Component)__instance).TryGetComponent(ref characterDrop)) { CharacterDropManager.UntrackCharacterDropInstance(characterDrop); } } } [HarmonyPatch(typeof(CharacterDrop), "OnDeath")] internal static class CharacterDropOnDeathPatch { private static bool Prefix(CharacterDrop __instance) { if (!PluginSettingsFacade.IsCharacterDomainEnabled()) { return true; } return !CharacterDropManager.TryHandleConfiguredDeath(__instance); } } [HarmonyPatch(typeof(CharacterDrop), "DropItems")] internal static class CharacterDropDropItemsPatch { private static void Prefix(ref List> drops, Vector3 centerPos, float dropArea) { //IL_0009: Unknown result type (might be due to invalid IL or missing references) if (PluginSettingsFacade.IsCharacterDomainEnabled()) { CharacterDropManager.ApplyGlobalDropInStack(ref drops, centerPos, dropArea); } } } [HarmonyPatch(typeof(Ragdoll), "Setup")] internal static class RagdollSetupMonsterInstantLootDropPatch { private static void Postfix(Ragdoll __instance, CharacterDrop characterDrop) { //IL_0053: 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_0079: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Unknown result type (might be due to invalid IL or missing references) if (!PluginSettingsFacade.IsMonsterInstantLootDropEnabled() || (Object)(object)characterDrop == (Object)null || !__instance.m_dropItems) { return; } ZNetView nview = __instance.m_nview; if ((Object)(object)nview == (Object)null || !nview.IsValid() || !nview.IsOwner()) { return; } ZDO zDO = nview.GetZDO(); if (zDO.GetInt(ZDOVars.s_drops, 0) > 0) { Vector3 val = __instance.GetAverageBodyPosition(); if ((Object)(object)__instance.m_lootSpawnJoint != (Object)null) { val = __instance.m_lootSpawnJoint.transform.position; } __instance.SpawnLoot(val); zDO.Set(ZDOVars.s_drops, 0, false); } } } [HarmonyPatch(typeof(ZoneSystem), "SpawnLocation", new Type[] { typeof(ZoneLocation), typeof(int), typeof(Vector3), typeof(Quaternion), typeof(SpawnMode), typeof(List) })] [HarmonyAfter(new string[] { "expand_world_data" })] internal static class ZoneSystemSpawnLocationContextPatch { private sealed class SpawnLocationContextState { public bool HasContext { get; set; } } private static void Prefix(ZoneLocation location, ref SpawnLocationContextState? __state) { __state = null; if (PluginSettingsFacade.IsSpawnerDomainEnabled() && !DropNSpawnPlugin.IsGameDataRefreshDeferred(DropNSpawnPlugin.ReloadDomain.Spawner)) { SpawnerManager.BeginLocationSpawnContext(location); __state = new SpawnLocationContextState { HasContext = true }; } } private static void Postfix(GameObject __result, SpawnLocationContextState? __state) { if (__state != null && __state.HasContext) { SpawnerManager.RecordSpawnedLocationRootProvenance(__result); } } private static void Finalizer(SpawnLocationContextState? __state) { if (__state != null && __state.HasContext) { SpawnerManager.EndLocationSpawnContext(); } } } [HarmonyPatch(typeof(DungeonGenerator), "Generate", new Type[] { typeof(SpawnMode) })] internal static class DungeonGeneratorGenerateContextPatch { private static void Prefix(DungeonGenerator __instance, ref bool __state) { __state = false; if (PluginSettingsFacade.IsSpawnerDomainEnabled() && !DropNSpawnPlugin.IsGameDataRefreshDeferred(DropNSpawnPlugin.ReloadDomain.Spawner)) { __state = SpawnerManager.TryBeginDerivedLocationSpawnContext(__instance); } } private static void Finalizer(bool __state) { if (__state) { SpawnerManager.EndLocationSpawnContext(); } } } [HarmonyPatch(typeof(DungeonGenerator), "Spawn")] internal static class DungeonGeneratorSpawnContextPatch { private static void Prefix(DungeonGenerator __instance, ref bool __state) { __state = false; if (PluginSettingsFacade.IsSpawnerDomainEnabled() && !DropNSpawnPlugin.IsGameDataRefreshDeferred(DropNSpawnPlugin.ReloadDomain.Spawner)) { __state = SpawnerManager.TryBeginDerivedLocationSpawnContext(__instance); } } private static void Finalizer(bool __state) { if (__state) { SpawnerManager.EndLocationSpawnContext(); } } } [HarmonyPatch(typeof(SpawnArea), "Awake")] internal static class SpawnAreaAwakePatch { private static void Postfix(SpawnArea __instance) { SpawnerManager.HandleSpawnAreaInstanceAwake(__instance); } } [HarmonyPatch(typeof(SpawnArea), "UpdateSpawn")] internal static class SpawnAreaUpdateSpawnPatch { private static bool Prefix(SpawnArea __instance) { if (!PluginSettingsFacade.IsSpawnerDomainEnabled()) { return true; } if (SpawnerManager.ShouldBlockClientSpawnerUpdate()) { return false; } return SpawnerManager.PrepareSpawnAreaForUpdate(__instance); } } [HarmonyPatch(typeof(SpawnArea), "SelectWeightedPrefab")] internal static class SpawnAreaSelectWeightedPrefabPatch { private static void Postfix(SpawnArea __instance, SpawnData __result) { if (PluginSettingsFacade.IsSpawnerDomainEnabled()) { SpawnerManager.RecordSelectedSpawnAreaPrefab(__instance, __result); } } } [HarmonyPatch(typeof(SpawnArea), "FindSpawnPoint")] internal static class SpawnAreaFindSpawnPointPatch { private static void Postfix(SpawnArea __instance, GameObject prefab, ref Vector3 point, bool __result) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) if (PluginSettingsFacade.IsSpawnerDomainEnabled()) { SpawnerManager.RecordSpawnAreaSpawnPoint(__instance, __result, point); if (__result) { SpawnerManager.InitializeSpawnAreaSpawnData(__instance, prefab, point); } } } } [HarmonyPatch(typeof(SpawnArea), "SpawnOne")] internal static class SpawnAreaSpawnOnePatch { private static readonly MethodInfo InstantiateGameObjectMethod = (from method in typeof(Object).GetMethods(BindingFlags.Static | BindingFlags.Public) where method.Name == "Instantiate" && method.IsGenericMethodDefinition select method).First(delegate(MethodInfo method) { ParameterInfo[] parameters = method.GetParameters(); return parameters.Length == 3 && parameters[2].ParameterType == typeof(Quaternion); }).MakeGenericMethod(typeof(GameObject)); private static void Prefix(SpawnArea __instance) { if (PluginSettingsFacade.IsSpawnerDomainEnabled()) { SpawnerManager.BeginSpawnAreaSpawnAttempt(__instance); } } [HarmonyTranspiler] private static IEnumerable Transpiler(IEnumerable instructions) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Expected O, but got Unknown //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Expected O, but got Unknown //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Expected O, but got Unknown //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Expected O, but got Unknown //IL_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Expected O, but got Unknown CodeMatcher val = new CodeMatcher(instructions, (ILGenerator)null); val.MatchForward(false, (CodeMatch[])(object)new CodeMatch[1] { new CodeMatch((OpCode?)OpCodes.Call, (object)InstantiateGameObjectMethod, (string)null) }); if (!val.IsValid) { DropNSpawnPlugin.DropNSpawnLogger.LogError((object)"Failed to locate SpawnArea.SpawnOne instantiate call for faction tracking."); return instructions; } return val.Advance(1).InsertAndAdvance((CodeInstruction[])(object)new CodeInstruction[1] { new CodeInstruction(OpCodes.Dup, (object)null) }).InsertAndAdvance((CodeInstruction[])(object)new CodeInstruction[1] { new CodeInstruction(OpCodes.Ldarg_0, (object)null) }) .InsertAndAdvance((CodeInstruction[])(object)new CodeInstruction[1] { new CodeInstruction(OpCodes.Call, Transpilers.EmitDelegate>((Action)RecordSpawnedObject).operand) }) .InstructionEnumeration(); } private static void Postfix(SpawnArea __instance, bool __result) { if (PluginSettingsFacade.IsSpawnerDomainEnabled()) { SpawnerManager.FinalizeSpawnAreaSpawnAttempt(__instance, __result); } } private static void RecordSpawnedObject(GameObject spawnedObject, SpawnArea spawnArea) { SpawnerManager.RecordDirectSpawnAreaSpawnedObject(spawnArea, spawnedObject); } } [HarmonyPatch(typeof(CreatureSpawner), "Awake")] internal static class CreatureSpawnerAwakePatch { private static void Postfix(CreatureSpawner __instance) { SpawnerManager.HandleCreatureSpawnerInstanceAwake(__instance); } } [HarmonyPatch(typeof(CreatureSpawner), "OnDestroy")] internal static class CreatureSpawnerOnDestroyPatch { private static void Prefix(CreatureSpawner __instance) { SpawnerManager.UntrackCreatureSpawnerInstance(__instance); } } [HarmonyPatch(typeof(CreatureSpawner), "UpdateSpawner")] internal static class CreatureSpawnerUpdateSpawnerPatch { private static bool Prefix(CreatureSpawner __instance) { if (!PluginSettingsFacade.IsSpawnerDomainEnabled()) { return true; } if (SpawnerManager.ShouldBlockClientSpawnerUpdate()) { return false; } return SpawnerManager.PrepareCreatureSpawnerForUpdate(__instance); } } [HarmonyPatch(typeof(CreatureSpawner), "Spawn")] internal static class CreatureSpawnerSpawnPatch { private static void Prefix(CreatureSpawner __instance) { //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_003f: 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) if (PluginSettingsFacade.IsSpawnerDomainEnabled()) { Vector3 position = ((Component)__instance).transform.position; float y = default(float); if ((Object)(object)ZoneSystem.instance != (Object)null && ZoneSystem.instance.FindFloor(position, ref y)) { position.y = y; } SpawnerManager.InitializeCreatureSpawnerSpawnData(__instance, __instance.m_creaturePrefab, position); } } private static void Postfix(CreatureSpawner __instance, ZNetView __result) { if (PluginSettingsFacade.IsSpawnerDomainEnabled()) { SpawnerManager.ApplyCreatureSpawnerSpawnOverrides(__instance, __result); } } } [HarmonyPatch(typeof(BaseAI), "Awake")] internal static class BaseAIAwakeFactionPatch { private static void Postfix(BaseAI __instance) { FactionIntegration.ApplyFromZdo(__instance); } } [HarmonyPatch(typeof(SpawnSystem), "Awake")] [HarmonyPriority(0)] internal static class SpawnSystemAwakePatch { private static void Prefix(SpawnSystem __instance) { SpawnSystemManager.PreAttachCompiledTableToAwakeningSystem(__instance); } private static void Postfix(SpawnSystem __instance) { SpawnSystemManager.OnSpawnSystemAwake(__instance); } } [HarmonyPatch(typeof(SpawnSystem), "OnDestroy")] internal static class SpawnSystemOnDestroyPatch { private static void Prefix(SpawnSystem __instance) { SpawnSystemManager.UntrackLiveSystem(__instance); } } [HarmonyPatch(typeof(SpawnSystem), "UpdateSpawning")] internal static class SpawnSystemUpdateSpawningRequiredGlobalKeyPatch { private static bool Prefix(SpawnSystem __instance, out bool __state) { __state = false; if (!PluginSettingsFacade.IsSpawnSystemDomainEnabled()) { return true; } if (SpawnSystemManager.ShouldBlockClientSpawnSystemUpdate(__instance)) { return false; } SpawnSystemManager.RefreshRuntimeTimeOfDayState(); __state = true; SpawnSystemManager.EnterRequiredGlobalKeyEvaluation(); return true; } private static Exception? Finalizer(bool __state, Exception? __exception) { if (__state) { SpawnSystemManager.ExitRequiredGlobalKeyEvaluation(); } return __exception; } } [HarmonyPatch(typeof(ZoneSystem), "GetGlobalKey", new Type[] { typeof(string) })] internal static class ZoneSystemGetGlobalKeyStringPatch { private static bool Prefix(ZoneSystem __instance, string name, ref bool __result) { if (!PluginSettingsFacade.IsSpawnSystemDomainEnabled()) { return true; } if (!SpawnSystemManager.TryEvaluateExtendedRequiredGlobalKey(__instance, name, out var result)) { return true; } __result = result; return false; } } [HarmonyPatch(typeof(ZoneSystem), "RPC_SetGlobalKey")] internal static class ZoneSystemRpcSetGlobalKeyPatch { private static void Prefix(ZoneSystem __instance, ref string name) { if (DropNSpawnPlugin.IsSourceOfTruth && PluginSettingsFacade.IsSpawnSystemDomainEnabled() && SpawnSystemManager.TryRewriteExtendedGlobalKeyMutation(__instance, name, out string rewrittenValue)) { name = rewrittenValue; } } } [HarmonyPatch(typeof(SpawnSystem), "Spawn")] internal static class SpawnSystemSpawnPatch { private static void Prefix(SpawnData critter, Vector3 spawnPoint, out bool __state) { //IL_0017: Unknown result type (might be due to invalid IL or missing references) __state = false; if (PluginSettingsFacade.IsSpawnSystemDomainEnabled() || SpawnSystemCustomDataSupport.HasPreparedPayload(critter)) { __state = true; SpawnSystemCustomDataSupport.InitializeSpawn(critter, spawnPoint); } } private static void Postfix(SpawnData critter, Vector3 spawnPoint, bool __state) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) if (__state) { SpawnSystemManager.ConsumeExtendedRequiredGlobalKeyAfterSpawn(critter); SpawnSystemCustomDataSupport.SpawnObjects(critter, spawnPoint); } } private static Exception? Finalizer(SpawnData critter, bool __state, Exception? __exception) { return __exception; } } internal static class GeneratedFileWriter { internal static bool WriteAllTextIfChanged(string path, string content) { if (path == null) { throw new ArgumentNullException("path"); } if (content == null) { content = ""; } string directoryName = Path.GetDirectoryName(path); if (!string.IsNullOrEmpty(directoryName)) { Directory.CreateDirectory(directoryName); } if (File.Exists(path) && string.Equals(File.ReadAllText(path), content, StringComparison.Ordinal)) { return false; } File.WriteAllText(path, content); return true; } } internal static class GeneratedArtifactWriter { internal static bool WriteText(string path, string content, string? logMessage = null, bool logOnlyWhenChanged = false) { bool flag = GeneratedFileWriter.WriteAllTextIfChanged(path, content); if (!string.IsNullOrWhiteSpace(logMessage) && (!logOnlyWhenChanged || flag)) { DropNSpawnPlugin.DropNSpawnLogger.LogInfo((object)logMessage); } return flag; } internal static void WriteTextAlways(string path, string content, string? logMessage = null) { string directoryName = Path.GetDirectoryName(path); if (!string.IsNullOrWhiteSpace(directoryName)) { Directory.CreateDirectory(directoryName); } File.WriteAllText(path, content ?? ""); if (!string.IsNullOrWhiteSpace(logMessage)) { DropNSpawnPlugin.DropNSpawnLogger.LogInfo((object)logMessage); } } } internal static class ReferenceRefreshSupport { internal const string CurrentReferenceLogicVersion = "2026-03-26-full-rewrite-v1"; internal static string ComputeStableHashForKeys(IEnumerable keys) { StringBuilder stringBuilder = new StringBuilder(); foreach (string item in (from key in (keys ?? Enumerable.Empty()).Select(NormalizeKey) where key.Length > 0 select key).OrderBy((string key) => key, StringComparer.OrdinalIgnoreCase)) { stringBuilder.AppendLine(item); } return ComputeStableHash(stringBuilder.ToString()); } internal static string SerializeReferenceSections(IEnumerable entries, Func getPrefabName, ISerializer serializer) { return PrefabOutputSections.SerializeReferenceSections(PrefabOutputSections.BuildSections(entries ?? Enumerable.Empty(), getPrefabName), serializer); } internal static HashSet ToNormalizedKeySet(IEnumerable keys) { HashSet hashSet = new HashSet(StringComparer.OrdinalIgnoreCase); foreach (string item in keys ?? Enumerable.Empty()) { string text = NormalizeKey(item); if (text.Length > 0) { hashSet.Add(text); } } return hashSet; } internal static string NormalizeKey(string? key) { return (key ?? "").Trim(); } internal static bool ShouldSkipAutoUpdate(string stateKey, string referencePath, string sourceSignature, string? logicVersion = null) { if (string.IsNullOrWhiteSpace(stateKey) || string.IsNullOrWhiteSpace(referencePath) || string.IsNullOrWhiteSpace(sourceSignature) || !File.Exists(referencePath)) { return false; } string autoUpdateStatePath = GetAutoUpdateStatePath(stateKey); if (!File.Exists(autoUpdateStatePath)) { return false; } try { string text = (logicVersion ?? "").Trim(); string[] array = File.ReadAllLines(autoUpdateStatePath); if (array.Length < 2) { return false; } string a = (array[0] ?? "").Trim(); string a2 = (array[1] ?? "").Trim(); string a3 = ((array.Length >= 4) ? (array[3] ?? "").Trim() : ""); if (text.Length > 0 && !string.Equals(a3, text, StringComparison.Ordinal)) { return false; } return string.Equals(a, sourceSignature, StringComparison.Ordinal) && string.Equals(a2, BuildReferenceFileStamp(referencePath), StringComparison.Ordinal); } catch { return false; } } internal static void RecordAutoUpdateState(string stateKey, string referencePath, string sourceSignature, string? precheckSignature = null, string? logicVersion = null) { if (!string.IsNullOrWhiteSpace(stateKey) && !string.IsNullOrWhiteSpace(referencePath) && !string.IsNullOrWhiteSpace(sourceSignature) && File.Exists(referencePath)) { string autoUpdateStatePath = GetAutoUpdateStatePath(stateKey); string content = sourceSignature.Trim() + Environment.NewLine + BuildReferenceFileStamp(referencePath) + Environment.NewLine + (precheckSignature ?? "").Trim() + Environment.NewLine + (logicVersion ?? "").Trim() + Environment.NewLine; GeneratedFileWriter.WriteAllTextIfChanged(autoUpdateStatePath, content); } } internal static string ComputeStableHash(string? value) { using SHA256 sHA = SHA256.Create(); byte[] bytes = Encoding.UTF8.GetBytes(value ?? ""); byte[] array = sHA.ComputeHash(bytes); StringBuilder stringBuilder = new StringBuilder(array.Length * 2); byte[] array2 = array; foreach (byte b in array2) { stringBuilder.Append(b.ToString("x2")); } return stringBuilder.ToString(); } private static string GetAutoUpdateStatePath(string stateKey) { string text = SanitizeFileName(stateKey); return Path.Combine(GetAutoUpdateStateDirectoryPath(), ".reference-state." + text + ".txt"); } private static string GetAutoUpdateStateDirectoryPath() { return Path.Combine(DropNSpawnPlugin.YamlConfigDirectoryPath, "cache"); } private static string BuildReferenceFileStamp(string referencePath) { FileInfo fileInfo = new FileInfo(referencePath); return $"{fileInfo.Length}:{fileInfo.LastWriteTimeUtc.Ticks}"; } private static string SanitizeFileName(string value) { StringBuilder stringBuilder = new StringBuilder(value.Length); HashSet hashSet = Path.GetInvalidFileNameChars().ToHashSet(); foreach (char c in value) { stringBuilder.Append(hashSet.Contains(c) ? '_' : c); } return stringBuilder.ToString(); } } internal enum ReferenceArtifactUpdateKind { None, Created, Updated } internal static class ReferenceArtifactLifecycle { internal static bool TryPlanUpdate(string stateKey, string referencePath, string sourceSignature, out ReferenceArtifactUpdateKind updateKind) { updateKind = ReferenceArtifactUpdateKind.None; if (!File.Exists(referencePath)) { updateKind = ReferenceArtifactUpdateKind.Created; return true; } if (ReferenceRefreshSupport.ShouldSkipAutoUpdate(stateKey, referencePath, sourceSignature, "2026-03-26-full-rewrite-v1")) { return false; } updateKind = ReferenceArtifactUpdateKind.Updated; return true; } internal static void RecordUpdate(string stateKey, string referencePath, string sourceSignature) { ReferenceRefreshSupport.RecordAutoUpdateState(stateKey, referencePath, sourceSignature, null, "2026-03-26-full-rewrite-v1"); } internal static string FormatAction(ReferenceArtifactUpdateKind updateKind) { if (updateKind != ReferenceArtifactUpdateKind.Created) { return "Updated"; } return "Created"; } } internal sealed class CharacterDropPrefabEntry { [YamlMember(Order = 1)] public string Prefab { get; set; } = ""; [YamlMember(Order = 2)] public bool Enabled { get; set; } = true; [YamlMember(Order = 3)] public ConditionsDefinition? Conditions { get; set; } [YamlMember(Order = 4)] public CharacterDropDefinition? CharacterDrop { get; set; } [YamlIgnore] public string RuleId { get; set; } = ""; [YamlIgnore] public string? SourcePath { get; set; } [YamlIgnore] public int SourceLine { get; set; } [YamlIgnore] public int SourceColumn { get; set; } } internal sealed class CharacterDropReferenceEntry { [YamlMember(Order = 1)] public string Prefab { get; set; } = ""; [YamlMember(Order = 2)] public CharacterDropDefinition? CharacterDrop { get; set; } } internal sealed class CharacterDropDefinition { [YamlMember(Order = 1)] public List? Drops { get; set; } } internal sealed class CharacterDropEntryDefinition { [YamlMember(Order = 1)] public string Item { get; set; } = ""; [YamlMember(Order = 2)] public IntRangeDefinition? Amount { get; set; } [YamlIgnore] public int? AmountMin { get; set; } [YamlIgnore] public int? AmountMax { get; set; } [YamlMember(Order = 3)] public float? Chance { get; set; } [YamlMember(Order = 4)] public bool? DontScale { get; set; } [YamlMember(Order = 5)] public bool? LevelMultiplier { get; set; } [YamlMember(Order = 6)] public bool? OnePerPlayer { get; set; } [YamlMember(Order = 7)] public int? AmountLimit { get; set; } [YamlMember(Order = 8)] public bool? DropInStack { get; set; } } internal sealed class CompiledCharacterDropDefinition { public string Fingerprint { get; set; } = ""; public GameObject Prefab { get; set; } public int AmountMin { get; set; } public int AmountMax { get; set; } public float Chance { get; set; } public bool DontScale { get; set; } public bool LevelMultiplier { get; set; } public bool OnePerPlayer { get; set; } public int? AmountLimit { get; set; } public bool DropInStack { get; set; } } internal sealed class CompiledCharacterDropRule { public CharacterDropPrefabEntry Entry { get; set; } public List Drops { get; } = new List(); } internal sealed class CachedCharacterRuntimeDropResolution { public bool HasMatchedRuntimeRule { get; set; } public CompiledCharacterDropDefinition[] Definitions { get; set; } = Array.Empty(); public List? OverrideDrops { get; set; } public bool HasCustomDropHandling { get; set; } } internal sealed class CharacterRuntimeDropCacheState { public bool IsCacheable { get; set; } = true; public bool UsesLevel { get; set; } public bool UsesFaction { get; set; } public bool UsesState { get; set; } public bool UsesTimeOfDay { get; set; } public bool UsesRequiredEnvironments { get; set; } public bool UsesInsidePlayerBase { get; set; } public string[] RequiredGlobalKeys { get; set; } = Array.Empty(); public string[] ForbiddenGlobalKeys { get; set; } = Array.Empty(); public Dictionary ResolutionsBySignature { get; } = new Dictionary(); } internal sealed class CharacterCompiledState { public static CharacterCompiledState Empty { get; } = new CharacterCompiledState(); public Dictionary> RuntimeRulesByPrefab { get; } = new Dictionary>(StringComparer.OrdinalIgnoreCase); public Dictionary RuntimeDropCachesByPrefab { get; } = new Dictionary(StringComparer.OrdinalIgnoreCase); public Dictionary> StaticDropsByPrefab { get; } = new Dictionary>(StringComparer.OrdinalIgnoreCase); public Dictionary> StaticBuiltDropsByPrefab { get; } = new Dictionary>(StringComparer.OrdinalIgnoreCase); } internal sealed class CharacterDropItemSnapshot { public GameObject? ItemPrefab { get; set; } public int AmountMin { get; set; } public int AmountMax { get; set; } public float Chance { get; set; } public bool OnePerPlayer { get; set; } public bool LevelMultiplier { get; set; } public bool DontScale { get; set; } } internal sealed class CharacterDropSnapshot { public GameObject Prefab { get; set; } public List Drops { get; set; } = new List(); public List BuiltDrops { get; set; } = new List(); } internal sealed class PendingCharacterDropSnapshotBuildState { public int GameDataSignature { get; set; } public int SnapshotSignature { get; set; } public string Source { get; set; } = ""; public List Prefabs { get; } = new List(); public List Snapshots { get; } = new List(); public Dictionary SnapshotsByPrefab { get; } = new Dictionary(StringComparer.OrdinalIgnoreCase); public int NextIndex { get; set; } } internal static class CharacterDropRuntime { private static readonly List Snapshots = new List(); private static readonly Dictionary SnapshotsByPrefab = new Dictionary(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary> LiveCharacterDropsByPrefab = new Dictionary>(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary LiveCharacterDropPrefabsByInstance = new Dictionary(); private static int? _lastProcessedSnapshotSignature; private static int? _lastProcessedGameDataSignature; private static int? _lastBootstrappedLiveObjectsSceneSignature; private static PendingCharacterDropSnapshotBuildState? _pendingSnapshotBuild; internal static void Reset() { Snapshots.Clear(); SnapshotsByPrefab.Clear(); LiveCharacterDropsByPrefab.Clear(); LiveCharacterDropPrefabsByInstance.Clear(); _lastProcessedSnapshotSignature = null; _lastProcessedGameDataSignature = null; _lastBootstrappedLiveObjectsSceneSignature = null; _pendingSnapshotBuild = null; } internal static bool HasSnapshots() { return Snapshots.Count > 0; } internal static IReadOnlyList GetSnapshots() { return Snapshots; } internal static bool HasSnapshot(string prefabName) { if (!string.IsNullOrWhiteSpace(prefabName)) { return SnapshotsByPrefab.ContainsKey(prefabName); } return false; } internal static bool TryGetSnapshot(string prefabName, out CharacterDropSnapshot? snapshot) { return SnapshotsByPrefab.TryGetValue(prefabName ?? "", out snapshot); } internal static bool IsGameDataAlreadyProcessed(int gameDataSignature) { return _lastProcessedGameDataSignature == gameDataSignature; } internal static bool NeedsSnapshotBuild(int snapshotSignature) { if (_lastProcessedSnapshotSignature == snapshotSignature) { return Snapshots.Count == 0; } return true; } internal static void ScheduleSnapshotBuild(string source, int gameDataSignature, int snapshotSignature, IEnumerable prefabs) { if (_pendingSnapshotBuild == null || _pendingSnapshotBuild.GameDataSignature != gameDataSignature || _pendingSnapshotBuild.SnapshotSignature != snapshotSignature) { PendingCharacterDropSnapshotBuildState pendingCharacterDropSnapshotBuildState = new PendingCharacterDropSnapshotBuildState(); pendingCharacterDropSnapshotBuildState.GameDataSignature = gameDataSignature; pendingCharacterDropSnapshotBuildState.SnapshotSignature = snapshotSignature; pendingCharacterDropSnapshotBuildState.Source = source; pendingCharacterDropSnapshotBuildState.Prefabs.AddRange(prefabs); _pendingSnapshotBuild = pendingCharacterDropSnapshotBuildState; } } internal static bool HasPendingSnapshotBuildWork() { return _pendingSnapshotBuild != null; } internal static bool ProcessPendingSnapshotBuildStep(double deadline, Func captureSnapshot, Action onCompleted) { PendingCharacterDropSnapshotBuildState pendingSnapshotBuild = _pendingSnapshotBuild; if (pendingSnapshotBuild == null) { return false; } if (pendingSnapshotBuild.NextIndex >= pendingSnapshotBuild.Prefabs.Count) { CompletePendingSnapshotBuild(pendingSnapshotBuild, onCompleted); return true; } GameObject val = pendingSnapshotBuild.Prefabs[pendingSnapshotBuild.NextIndex]; pendingSnapshotBuild.NextIndex++; CharacterDropSnapshot characterDropSnapshot = (((Object)(object)val != (Object)null) ? captureSnapshot(val) : null); if (_pendingSnapshotBuild == null || _pendingSnapshotBuild != pendingSnapshotBuild) { return true; } if (characterDropSnapshot != null) { pendingSnapshotBuild.Snapshots.Add(characterDropSnapshot); if (!pendingSnapshotBuild.SnapshotsByPrefab.ContainsKey(((Object)characterDropSnapshot.Prefab).name)) { pendingSnapshotBuild.SnapshotsByPrefab.Add(((Object)characterDropSnapshot.Prefab).name, characterDropSnapshot); } } if (pendingSnapshotBuild.NextIndex >= pendingSnapshotBuild.Prefabs.Count && Time.realtimeSinceStartupAsDouble <= deadline) { CompletePendingSnapshotBuild(pendingSnapshotBuild, onCompleted); } return true; } internal static void CaptureSnapshotsIfNeeded(IEnumerable prefabs, Func captureSnapshot) { if (Snapshots.Count > 0) { return; } foreach (GameObject prefab in prefabs) { CharacterDropSnapshot characterDropSnapshot = captureSnapshot(prefab); if (characterDropSnapshot != null) { Snapshots.Add(characterDropSnapshot); if (!SnapshotsByPrefab.ContainsKey(((Object)characterDropSnapshot.Prefab).name)) { SnapshotsByPrefab.Add(((Object)characterDropSnapshot.Prefab).name, characterDropSnapshot); } } } } internal static void RegisterLiveCharacterDrop(CharacterDrop characterDrop, string prefabName) { if ((Object)(object)characterDrop == (Object)null || string.IsNullOrWhiteSpace(prefabName)) { return; } if (LiveCharacterDropPrefabsByInstance.TryGetValue(characterDrop, out string value)) { if (string.Equals(value, prefabName, StringComparison.OrdinalIgnoreCase)) { return; } UnregisterLiveCharacterDrop(characterDrop, value); } LiveCharacterDropPrefabsByInstance[characterDrop] = prefabName; if (!LiveCharacterDropsByPrefab.TryGetValue(prefabName, out HashSet value2)) { value2 = new HashSet(); LiveCharacterDropsByPrefab[prefabName] = value2; } value2.Add(characterDrop); } internal static bool TryGetRegisteredPrefabName(CharacterDrop characterDrop, out string prefabName) { return LiveCharacterDropPrefabsByInstance.TryGetValue(characterDrop, out prefabName); } internal static void UnregisterLiveCharacterDrop(CharacterDrop characterDrop, string prefabName) { LiveCharacterDropPrefabsByInstance.Remove(characterDrop); if (LiveCharacterDropsByPrefab.TryGetValue(prefabName, out HashSet value)) { value.Remove(characterDrop); if (value.Count == 0) { LiveCharacterDropsByPrefab.Remove(prefabName); } } } internal static void BootstrapRegisteredCharacterDropsIfNeeded(int sceneSignature, IEnumerable liveCharacterDrops, Func getPrefabName, Func shouldRegisterPrefab, ISet? additionalPrefabs = null, bool forceRescan = false) { if (!forceRescan && _lastBootstrappedLiveObjectsSceneSignature == sceneSignature) { return; } CleanupRegisteredCharacterDrops(); foreach (CharacterDrop liveCharacterDrop in liveCharacterDrops) { if (!((Object)(object)liveCharacterDrop == (Object)null) && !((Object)(object)((Component)liveCharacterDrop).gameObject == (Object)null)) { string text = getPrefabName(liveCharacterDrop); if (text.Length != 0 && (shouldRegisterPrefab(text) || (additionalPrefabs != null && additionalPrefabs.Contains(text)))) { RegisterLiveCharacterDrop(liveCharacterDrop, text); } } } _lastBootstrappedLiveObjectsSceneSignature = sceneSignature; } internal static IReadOnlyList GetRegisteredCharacterDrops(HashSet? dirtyPrefabs = null) { CleanupRegisteredCharacterDrops(); HashSet visited = new HashSet(); List list = new List(); if (dirtyPrefabs == null) { foreach (HashSet value2 in LiveCharacterDropsByPrefab.Values) { AddRegisteredCharacterDrops(value2, visited, list); } return list; } foreach (string dirtyPrefab in dirtyPrefabs) { if (LiveCharacterDropsByPrefab.TryGetValue(dirtyPrefab, out HashSet value)) { AddRegisteredCharacterDrops(value, visited, list); } } return list; } private static void AddRegisteredCharacterDrops(IEnumerable characterDrops, ISet visited, ICollection registered) { foreach (CharacterDrop item in characterDrops ?? Enumerable.Empty()) { if ((Object)(object)item != (Object)null && (Object)(object)((Component)item).gameObject != (Object)null && visited.Add(item)) { registered.Add(item); } } } private static void CleanupRegisteredCharacterDrops() { List list = null; foreach (CharacterDrop item in LiveCharacterDropPrefabsByInstance.Keys.ToList()) { if ((Object)(object)item == (Object)null || (Object)(object)((Component)item).gameObject == (Object)null) { if (list == null) { list = new List(); } list.Add(item); } } if (list == null) { return; } foreach (CharacterDrop item2 in list) { if (LiveCharacterDropPrefabsByInstance.TryGetValue(item2, out string value)) { UnregisterLiveCharacterDrop(item2, value); } } } private static void CompletePendingSnapshotBuild(PendingCharacterDropSnapshotBuildState buildState, Action onCompleted) { if (_pendingSnapshotBuild == null || _pendingSnapshotBuild != buildState) { return; } Snapshots.Clear(); Snapshots.AddRange(buildState.Snapshots); SnapshotsByPrefab.Clear(); foreach (var (key, value) in buildState.SnapshotsByPrefab) { SnapshotsByPrefab[key] = value; } _lastBootstrappedLiveObjectsSceneSignature = null; _pendingSnapshotBuild = null; _lastProcessedSnapshotSignature = buildState.SnapshotSignature; _lastProcessedGameDataSignature = buildState.GameDataSignature; onCompleted(buildState.Source, buildState.GameDataSignature, buildState.SnapshotSignature); } } internal static class CharacterDropManager { private sealed class ResolvedConfiguredDrop { public GameObject Prefab { get; set; } public int Amount { get; set; } public bool DropInStack { get; set; } } private sealed class CharacterConfigurationState { public List Configuration { get; } = new List(); public Dictionary> ActiveEntriesByPrefab { get; } = new Dictionary>(StringComparer.OrdinalIgnoreCase); public HashSet PrefabsWithCharacterDropOverrides { get; } = new HashSet(StringComparer.OrdinalIgnoreCase); public Dictionary CurrentEntrySignaturesByPrefab { get; set; } = new Dictionary(StringComparer.OrdinalIgnoreCase); public string ConfigurationSignature { get; set; } = ""; } private sealed class CharacterRuntimeContextSnapshot { public int Frame { get; set; } public int TimeOfDayPhaseMarker { get; set; } public string EnvironmentName { get; set; } = ""; public Dictionary GlobalKeyStates { get; } = new Dictionary(StringComparer.Ordinal); } private sealed class CharacterApplyOperations : StandardBaselineDesiredStateOperations { public static CharacterApplyOperations Instance { get; } = new CharacterApplyOperations(); public override string DomainKey => "character"; public override BaselineDesiredStateCapabilities Capabilities => BaselineDesiredStateCapabilities.Validation | BaselineDesiredStateCapabilities.StaticBaseline | BaselineDesiredStateCapabilities.StaticApply | BaselineDesiredStateCapabilities.LiveApply | BaselineDesiredStateCapabilities.StaticRollback; public override void Validate(CharacterDesiredState desiredState) { ValidateCharacterDesiredState(desiredState); } public override void RestoreStaticBaseline(CharacterDesiredState desiredState) { RestoreCharacterStaticBaseline(desiredState); } public override void ApplyDesiredStateToStaticBaseline(CharacterDesiredState desiredState) { ApplyCharacterDesiredStateToStaticBaseline(desiredState); } public override void ApplyDesiredStateToLive(CharacterDesiredState desiredState) { ApplyCharacterDesiredStateToLive(desiredState); } public override void Commit(CharacterDesiredState desiredState) { RecordAppliedState(desiredState.GameDataSignature, desiredState.DomainEnabled, desiredState.CurrentEntrySignatures, desiredState.EffectiveConfigurationSignature); } } private sealed class CharacterDesiredState { public StandardDomainApplyPlan ApplyPlan { get; set; } public CharacterCompiledState CompiledState { get; set; } = CharacterCompiledState.Empty; public int GameDataSignature { get; set; } public string EffectiveConfigurationSignature { get; set; } = ""; public Dictionary CurrentEntrySignatures { get; set; } = EmptyEntrySignatures; public bool DomainEnabled { get; set; } } private const string ReferenceAutoUpdateStateKey = "character"; internal static readonly DomainModuleDefinition Module = new DomainModuleDefinition(new DomainModuleOptions { DomainKey = "character", ReloadDomain = DropNSpawnPlugin.ReloadDomain.Character, ManifestSettingKey = "character_yaml", ManifestPriority = 99, ShouldReloadForPath = ShouldReloadForPath, Reload = ReloadConfiguration, InitializeRuntime = Initialize, OnGameDataReady = OnGameDataReady, HandleExpandWorldDataReady = HandleExpandWorldDataReady, DtoVersion = 10, TransportProfile = DomainTransportProfile.SmallConfig, DisplayName = "character", CacheDirectoryName = "character", ClientRequestPriority = 10, KeySelector = (CharacterDropPrefabEntry entry) => entry.RuleId, ApplyPayloadAction = ApplySyncedPayload, HasPendingSnapshotBuildWork = HasPendingSnapshotBuildWork, ProcessPendingSnapshotBuildStep = ProcessPendingSnapshotBuildStep, BeforeClientManifestChanged = MarkSyncedPayloadPending, OnClientAuthorityCutover = EnterPendingSyncedPayloadState }); private static readonly object Sync = new object(); private static readonly IDeserializer Deserializer = new DeserializerBuilder().WithNamingConvention(CamelCaseNamingConvention.Instance).Build(); private static readonly ISerializer Serializer = new SerializerBuilder().WithNamingConvention(CamelCaseNamingConvention.Instance).ConfigureDefaultValuesHandling(DefaultValuesHandling.OmitNull | DefaultValuesHandling.OmitDefaults).Build(); private static CharacterConfigurationState RuntimeState = new CharacterConfigurationState(); private static readonly InvalidEntryDiagnostics InvalidEntryWarnings = new InvalidEntryDiagnostics(); private static readonly FieldInfo? DropsEnabledField = AccessTools.Field(typeof(CharacterDrop), "m_dropsEnabled"); [ThreadStatic] private static CharacterDrop? OnePerPlayerScopeCharacterDrop; [ThreadStatic] private static int OnePerPlayerScopeDepth; private static bool _initialized; private static CharacterCompiledState _compiledState = CharacterCompiledState.Empty; private static string _compiledStateConfigurationSignature = ""; private static int? _compiledStateGameDataSignature; private static bool _referenceArtifactsAutoRefreshConsumed; private static readonly Dictionary _lastAppliedEntrySignaturesByPrefab = new Dictionary(StringComparer.OrdinalIgnoreCase); private static string _lastAppliedConfigurationSignature = ""; private static int? _lastAppliedGameDataSignature; private static bool? _lastAppliedDomainEnabled; private static bool _lastAppliedSynchronizedPayloadReady; private static bool _synchronizedPayloadReady; private static int? _lastCommittedAuthorityEpoch; private static CharacterRuntimeContextSnapshot? _runtimeContextSnapshot; private static int _cachedFrameGameDataSignatureFrame = -1; private static int _cachedFrameGameDataSignatureValue; private const string MockPrefabPrefix = "JVLmock_"; private const float ConfiguredDropGroundOffset = 0.2f; private const int MaxCachedRuntimeDropResolutionsPerPrefab = 32; private static readonly DomainConfigurationRuntime ConfigurationRuntime = new DomainConfigurationRuntime(new DomainLoadHooks(ParseLocalConfigurationDocuments, BuildSyncedConfigurationState, CommitSyncedConfigurationState, RejectLocalConfigurationPayload, (CharacterConfigurationState state) => state.Configuration.Count, LogPartiallyAcceptedLocalConfiguration, LogLocalConfigurationLoaded, OnSourceOfTruthPayloadUnchanged, delegate { ConfigurationDomainHost.PublishSyncedPayload(DropNSpawnPlugin.IsSourceOfTruth, Descriptor, RuntimeState.Configuration, RuntimeState.ConfigurationSignature); }), new DomainSyncHooks(delegate(out List configuration, out string payloadToken) { return ConfigurationDomainHost.TryGetSyncedEntries(Descriptor, out configuration, out payloadToken); }, (string payloadToken) => ConfigurationDomainHost.ShouldSkipSyncedPayload(LoadState, payloadToken, Volatile.Read(in _synchronizedPayloadReady)), BuildSyncedConfigurationState, CommitSyncedConfigurationState, (CharacterConfigurationState state) => state.ActiveEntriesByPrefab.Count, "ServerSync:DropNSpawnCharacter", MarkSyncedPayloadPending, LogSyncedCharacterConfigurationLoaded, LogSyncedCharacterConfigurationFailure)); private static readonly Dictionary EmptyEntrySignatures = new Dictionary(StringComparer.OrdinalIgnoreCase); internal static DomainDescriptor Descriptor => Module.DescriptorTyped; internal static DomainTransportMetadata TransportMetadata => Module.TransportMetadataTyped; private static DomainLoadState LoadState => ConfigurationRuntime.LoadState; private static string ReferenceConfigurationPath => Path.Combine(DropNSpawnPlugin.YamlConfigDirectoryPath, PluginSettingsFacade.GetYamlDomainFilePrefix("character") + ".reference.yml"); private static string PrimaryOverrideConfigurationPathYml => Path.Combine(DropNSpawnPlugin.YamlConfigDirectoryPath, PluginSettingsFacade.GetYamlDomainFilePrefix("character") + ".yml"); private static string PrimaryOverrideConfigurationPathYaml => Path.Combine(DropNSpawnPlugin.YamlConfigDirectoryPath, PluginSettingsFacade.GetYamlDomainFilePrefix("character") + ".yaml"); private static string FullScaffoldConfigurationPath => Path.Combine(DropNSpawnPlugin.YamlConfigDirectoryPath, PluginSettingsFacade.GetYamlDomainFilePrefix("character") + ".full.yml"); internal static bool ShouldReloadForPath(string? path) { if (PluginSettingsFacade.IsEligibleOverrideConfigurationPath(path)) { return IsOverrideConfigurationFileName(Path.GetFileName(path ?? "")); } return false; } private static bool ShouldApplyLocally() { return PluginSettingsFacade.IsCharacterDomainEnabled(); } internal static void Initialize() { lock (Sync) { if (!_initialized) { LoadConfiguration(); _initialized = true; } } } internal static void ReloadConfiguration() { lock (Sync) { LoadConfiguration(); ApplyIfReady(); } } internal static void MarkSyncedPayloadPending() { lock (Sync) { ConfigurationRuntime.MarkSyncedPayloadPending(DropNSpawnPlugin.IsSourceOfTruth, delegate { Volatile.Write(ref _synchronizedPayloadReady, value: false); }); } } internal static void EnterPendingSyncedPayloadState() { lock (Sync) { Dictionary previousEntrySignatures = CloneCurrentEntrySignaturesByPrefab(); HashSet previouslyAppliedPrefabs = BuildLastAppliedPrefabs(); ConfigurationRuntime.EnterPendingSyncedPayloadState(DropNSpawnPlugin.IsSourceOfTruth, ResetLoadedConfigurationState, delegate { RuntimeState.ConfigurationSignature = ""; _lastAppliedSynchronizedPayloadReady = false; RestoreSnapshots(previouslyAppliedPrefabs); RestoreTrackedCharacterDrops(previouslyAppliedPrefabs); RefreshVneiCompatibility(previousEntrySignatures); }); } } private static bool CanUseCurrentRuntimeState() { if (!DropNSpawnPlugin.IsSourceOfTruth && !Volatile.Read(in _synchronizedPayloadReady)) { return _lastCommittedAuthorityEpoch == NetworkPayloadSyncSupport.CurrentAuthorityEpoch; } return true; } private static HashSet BuildLastAppliedPrefabs() { HashSet hashSet = new HashSet(StringComparer.OrdinalIgnoreCase); if (_lastAppliedDomainEnabled != true) { return hashSet; } foreach (string key in _lastAppliedEntrySignaturesByPrefab.Keys) { hashSet.Add(key); } return hashSet; } internal static bool HandleExpandWorldDataReady() { lock (Sync) { if (!DropNSpawnPlugin.IsSourceOfTruth) { return false; } string text = NetworkPayloadSyncSupport.ComputeCharacterConfigurationSignature(RuntimeState.Configuration); if (string.Equals(text, RuntimeState.ConfigurationSignature, StringComparison.Ordinal)) { return false; } RuntimeState.ConfigurationSignature = text; ConfigurationDomainHost.PublishSyncedPayload(DropNSpawnPlugin.IsSourceOfTruth, Descriptor, RuntimeState.Configuration, RuntimeState.ConfigurationSignature); ApplyIfReady(); return true; } } internal static void ApplySyncedPayload() { lock (Sync) { Dictionary previousEntrySignatures = CloneCurrentEntrySignaturesByPrefab(); ConfigurationRuntime.ApplySyncedPayload(delegate { RefreshVneiCompatibility(previousEntrySignatures, CloneCurrentEntrySignaturesByPrefab()); ApplyIfReady(); }); } } internal static void OnGameDataReady(string source) { lock (Sync) { if (!_initialized) { Initialize(); } if (!IsGameDataReady()) { return; } int gameDataSignature = ComputeGameDataSignature(); if (!CharacterDropRuntime.IsGameDataAlreadyProcessed(gameDataSignature)) { int snapshotSignature = ComputeSnapshotSignature(); if (CharacterDropRuntime.NeedsSnapshotBuild(snapshotSignature)) { CharacterDropRuntime.ScheduleSnapshotBuild(source, gameDataSignature, snapshotSignature, EnumerateRelevantPrefabs()); } else { CompleteGameDataReadyLocked(source, gameDataSignature, snapshotSignature); } } } } internal static bool HasPendingSnapshotBuildWork() { lock (Sync) { return CharacterDropRuntime.HasPendingSnapshotBuildWork(); } } internal static bool ProcessPendingSnapshotBuildStep(double deadline) { lock (Sync) { return CharacterDropRuntime.ProcessPendingSnapshotBuildStep(deadline, CaptureSnapshot, CompleteGameDataReadyLocked); } } private static bool IsGameDataReady() { if ((Object)(object)ZNetScene.instance != (Object)null) { return (Object)(object)ObjectDB.instance != (Object)null; } return false; } private static bool EnsurePrimaryOverrideConfigurationFileExists() { if (DomainConfigurationFileSupport.HasAnyOverrideConfigurationFile("character", PrimaryOverrideConfigurationPathYml, PrimaryOverrideConfigurationPathYaml)) { return false; } if (!IsGameDataReady() || !CharacterDropRuntime.HasSnapshots()) { return false; } GeneratedArtifactWriter.WriteTextAlways(PrimaryOverrideConfigurationPathYml, BuildPrimaryOverrideConfigurationTemplate(), "Created character override configuration at " + PrimaryOverrideConfigurationPathYml + "."); return true; } private static void LoadConfiguration() { Dictionary previousEntrySignatures = CloneCurrentEntrySignaturesByPrefab(); if (DropNSpawnPlugin.IsSourceOfTruth) { EnsurePrimaryOverrideConfigurationFileExists(); if (ConfigurationRuntime.ReloadSourceOfTruth(EnumerateOverrideConfigurationPaths().ToList()) == DomainReloadOutcome.Loaded) { RefreshVneiCompatibility(previousEntrySignatures); } } else if (ConfigurationRuntime.ReloadSynced() == DomainReloadOutcome.Loaded) { RefreshVneiCompatibility(previousEntrySignatures, CloneCurrentEntrySignaturesByPrefab()); } } private static void CompleteGameDataReadyLocked(string source, int gameDataSignature, int snapshotSignature) { if (DropNSpawnPlugin.IsSourceOfTruth && !_referenceArtifactsAutoRefreshConsumed) { EnsureReferenceArtifactsUpToDate(); _referenceArtifactsAutoRefreshConsumed = true; } if (DropNSpawnPlugin.IsSourceOfTruth && EnsurePrimaryOverrideConfigurationFileExists()) { LoadConfiguration(); } ApplyIfReady(); } private static void ResetLoadedConfigurationState() { RuntimeState = new CharacterConfigurationState(); InvalidEntryWarnings.Clear(); _compiledState = CharacterCompiledState.Empty; _compiledStateConfigurationSignature = ""; _compiledStateGameDataSignature = null; _cachedFrameGameDataSignatureFrame = -1; _cachedFrameGameDataSignatureValue = 0; Volatile.Write(ref _synchronizedPayloadReady, value: false); } internal static void ResetWorldRuntimeState() { lock (Sync) { HashSet hashSet = BuildLastAppliedPrefabs(); RestoreSnapshots(hashSet); RestoreTrackedCharacterDrops(hashSet); CharacterDropRuntime.Reset(); _compiledState = CharacterCompiledState.Empty; _compiledStateConfigurationSignature = ""; _compiledStateGameDataSignature = null; _lastAppliedEntrySignaturesByPrefab.Clear(); _lastAppliedConfigurationSignature = ""; _lastAppliedGameDataSignature = null; _lastAppliedDomainEnabled = null; _lastAppliedSynchronizedPayloadReady = false; _runtimeContextSnapshot = null; _cachedFrameGameDataSignatureFrame = -1; _cachedFrameGameDataSignatureValue = 0; OnePerPlayerScopeCharacterDrop = null; OnePerPlayerScopeDepth = 0; } } private static List CloneAndNormalizeConfigurationEntries(List? configuration, string sourceName) { List list = NetworkPayloadSyncSupport.CloneEntries(Descriptor, configuration); foreach (CharacterDropPrefabEntry item in list) { NormalizeEntry(item); item.SourcePath = (string.IsNullOrWhiteSpace(item.SourcePath) ? sourceName : item.SourcePath); } return list; } private static List PrepareLocalConfigurationEntries(List? configuration, string sourceName, List warnings) { List list = CloneAndNormalizeConfigurationEntries(configuration, sourceName); List list2 = new List(); foreach (CharacterDropPrefabEntry item in list) { if (TryAcceptLocalConfigurationEntry(item, warnings)) { list2.Add(item); } } return list2; } private static bool TryAcceptLocalConfigurationEntry(CharacterDropPrefabEntry entry, List warnings) { if (!entry.Enabled) { return true; } string text = CreateConfigurationContext(entry); bool flag = entry.CharacterDrop != null; if (string.IsNullOrWhiteSpace(entry.Prefab)) { warnings.Add("Entry '" + text + "' is missing required prefab."); return false; } if (!flag) { warnings.Add("Entry '" + text + "' does not define characterDrop."); return false; } if (!TryResolveConfiguredCharacterPrefab(entry.Prefab, out var hasCharacterComponent, out var hasCharacterDropComponent)) { warnings.Add("Entry '" + text + "' references unknown character prefab '" + entry.Prefab + "'."); return false; } if (!hasCharacterComponent) { warnings.Add("Entry '" + text + "' references '" + entry.Prefab + "', but it is not a Character prefab."); return false; } if (flag && !hasCharacterDropComponent) { warnings.Add("Entry '" + text + "' references '" + entry.Prefab + "', but it is not a CharacterDrop prefab."); return false; } return true; } private static bool TryResolveConfiguredCharacterPrefab(string prefabName, out bool hasCharacterComponent, out bool hasCharacterDropComponent) { hasCharacterComponent = true; hasCharacterDropComponent = true; if ((Object)(object)ZNetScene.instance == (Object)null || string.IsNullOrWhiteSpace(prefabName)) { return true; } GameObject prefab = ZNetScene.instance.GetPrefab(prefabName.Trim()); if ((Object)(object)prefab == (Object)null) { hasCharacterComponent = false; hasCharacterDropComponent = false; return false; } hasCharacterComponent = (Object)(object)prefab.GetComponent() != (Object)null; hasCharacterDropComponent = (Object)(object)prefab.GetComponent() != (Object)null; return true; } private static CharacterConfigurationState BuildSyncedConfigurationState(List configuration, string sourceName) { using (BeginInvalidEntryWarningSuppressionForSyncedClientBuild(sourceName)) { CharacterConfigurationState characterConfigurationState = new CharacterConfigurationState(); foreach (CharacterDropPrefabEntry item in CloneAndNormalizeConfigurationEntries(configuration, sourceName)) { if (!string.IsNullOrWhiteSpace(item.Prefab)) { RemoveEffectiveConfigurationEntry(characterConfigurationState.Configuration, characterConfigurationState.ActiveEntriesByPrefab, item.Prefab, item.RuleId); if (item.Enabled) { characterConfigurationState.Configuration.Add(item); GetOrCreateActiveEntries(characterConfigurationState.ActiveEntriesByPrefab, item.Prefab).Add(item); } } } RebuildCharacterDropOverridePrefabSet(characterConfigurationState.ActiveEntriesByPrefab, characterConfigurationState.PrefabsWithCharacterDropOverrides); characterConfigurationState.ConfigurationSignature = NetworkPayloadSyncSupport.ComputeCharacterConfigurationSignature(characterConfigurationState.Configuration); characterConfigurationState.CurrentEntrySignaturesByPrefab = BuildActiveEntrySignaturesByPrefab(characterConfigurationState.ActiveEntriesByPrefab); return characterConfigurationState; } } private static void CommitSyncedConfigurationState(CharacterConfigurationState state, string payloadToken) { ResetLoadedConfigurationState(); RuntimeState = state; LoadState.LastLoadedPayload = payloadToken; LoadState.LastRejectedPayload = ""; LoadState.PendingStrictPayload = ""; LoadState.LastRejectedValidationKey = ""; Volatile.Write(ref _synchronizedPayloadReady, value: true); _lastCommittedAuthorityEpoch = (DropNSpawnPlugin.IsSourceOfTruth ? ((int?)null) : new int?(NetworkPayloadSyncSupport.CurrentAuthorityEpoch)); } private static LocalLoadResult ParseLocalConfigurationDocuments(List documents) { return ConfigurationLoadSupport.ParseLocalConfigurationDocuments(documents, ParseConfiguration, PrepareLocalConfigurationEntries, FormatYamlExceptionLocation, "Character override YAML must start with a root list like '- prefab: ...'."); } private static void RejectLocalConfigurationPayload(string payload, IEnumerable errors) { ConfigurationDomainHost.RejectLocalConfigurationPayload(LoadState, payload, errors, "character"); } private static void RebuildCharacterDropOverridePrefabSet(Dictionary> activeEntriesByPrefab, HashSet target) { target.Clear(); foreach (var (item, source) in activeEntriesByPrefab) { if (source.Any((CharacterDropPrefabEntry entry) => entry.CharacterDrop != null)) { target.Add(item); } } } private static bool RemoveEffectiveConfigurationEntry(List configuration, Dictionary> activeEntriesByPrefab, string prefabName, string ruleId) { bool result = false; for (int num = configuration.Count - 1; num >= 0; num--) { CharacterDropPrefabEntry characterDropPrefabEntry = configuration[num]; if (string.Equals(characterDropPrefabEntry.Prefab, prefabName, StringComparison.OrdinalIgnoreCase) && string.Equals(characterDropPrefabEntry.RuleId, ruleId, StringComparison.Ordinal)) { configuration.RemoveAt(num); result = true; } } if (activeEntriesByPrefab.TryGetValue(prefabName, out List value)) { for (int num2 = value.Count - 1; num2 >= 0; num2--) { if (string.Equals(value[num2].RuleId, ruleId, StringComparison.Ordinal)) { value.RemoveAt(num2); result = true; } } if (value.Count == 0) { activeEntriesByPrefab.Remove(prefabName); } } return result; } private static ConfigurationLoadSupport.ParsedLocalConfiguration ParseConfiguration(string yaml, string? sourcePath) { ConfigurationLoadSupport.ParsedLocalConfiguration parsedLocalConfiguration = new ConfigurationLoadSupport.ParsedLocalConfiguration(); if (string.IsNullOrWhiteSpace(yaml)) { return parsedLocalConfiguration; } checked { using StringReader input = new StringReader(yaml); YamlStream yamlStream = new YamlStream(); yamlStream.Load(input); if (yamlStream.Documents.Count == 0) { return parsedLocalConfiguration; } foreach (YamlNode child in ((yamlStream.Documents[0].RootNode as YamlSequenceNode) ?? throw new YamlException(yamlStream.Documents[0].RootNode.Start, yamlStream.Documents[0].RootNode.End, "Character override YAML root must be a sequence.")).Children) { if (!(child is YamlMappingNode yamlMappingNode)) { parsedLocalConfiguration.Warnings.Add("Skipped character YAML node at " + FormatYamlNodeLocation(sourcePath, child.Start) + ". Expected a list item object like '- prefab: Fox' but found " + DescribeYamlNode(child) + "."); continue; } try { string input2 = SerializeYamlNode(yamlMappingNode); CharacterDropPrefabEntry characterDropPrefabEntry = Deserializer.Deserialize(input2) ?? new CharacterDropPrefabEntry(); characterDropPrefabEntry.SourceLine = (int)yamlMappingNode.Start.Line; characterDropPrefabEntry.SourceColumn = (int)yamlMappingNode.Start.Column; parsedLocalConfiguration.Configuration.Add(characterDropPrefabEntry); } catch (Exception ex) { parsedLocalConfiguration.Warnings.Add("Skipped invalid character entry at " + FormatYamlNodeLocation(sourcePath, yamlMappingNode.Start) + ". " + FormatEntryParseFailure(ex)); } } return parsedLocalConfiguration; } } private static string SerializeYamlNode(YamlNode node) { YamlStream yamlStream = new YamlStream(new YamlDocument(node)); using StringWriter stringWriter = new StringWriter(CultureInfo.InvariantCulture); yamlStream.Save(stringWriter, assignAnchors: false); return stringWriter.ToString(); } private static string DescribeYamlNode(YamlNode node) { if (node is YamlScalarNode yamlScalarNode) { string text = yamlScalarNode.Value ?? ""; if (text.Length != 0) { return "scalar '" + text + "'"; } return "an empty scalar"; } if (node is YamlSequenceNode) { return "a nested sequence"; } if (node is YamlMappingNode) { return "a mapping"; } return "an unknown YAML node"; } private static string FormatYamlNodeLocation(string? sourcePath, Mark mark) { string text = (string.IsNullOrWhiteSpace(sourcePath) ? "inline YAML" : Path.GetFileName(sourcePath)); if (mark.Line > 0) { text = text + ":" + mark.Line.ToString(CultureInfo.InvariantCulture); } return text; } private static string FormatEntryParseFailure(Exception ex) { if (ex is YamlException ex2) { return ex2.Message; } return ex.Message; } private static void NormalizeEntry(CharacterDropPrefabEntry entry) { entry.Prefab = (entry.Prefab ?? "").Trim(); if (entry.CharacterDrop != null) { CharacterDropDefinition characterDrop = entry.CharacterDrop; if (characterDrop.Drops == null) { List list = (characterDrop.Drops = new List()); } foreach (CharacterDropEntryDefinition drop in entry.CharacterDrop.Drops) { drop.Item = (drop.Item ?? "").Trim(); IntRangeDefinition? amount = drop.Amount; if (amount != null && amount.HasValues()) { drop.AmountMin = RangeFormatting.GetMin(drop.Amount, drop.AmountMin); drop.AmountMax = RangeFormatting.GetMax(drop.Amount, drop.AmountMin, drop.AmountMax); } } } entry.RuleId = NormalizeOptionalRuleId(entry.RuleId) ?? BuildRuleId(entry); } private static string BuildRuleId(CharacterDropPrefabEntry entry) { CharacterDropPrefabEntry entry2 = new CharacterDropPrefabEntry { Prefab = entry.Prefab, Enabled = true, Conditions = entry.Conditions, CharacterDrop = entry.CharacterDrop }; return entry.Prefab + ":" + NetworkPayloadSyncSupport.ComputeCharacterEntryIdentitySignature(entry2); } private static string? NormalizeOptionalRuleId(string? ruleId) { if (ruleId == null) { return null; } string text = ruleId.Trim(); if (text.Length != 0) { return text; } return null; } private static bool HasCustomDropHandling(IReadOnlyList? drops) { return drops?.Any((CompiledCharacterDropDefinition drop) => (drop.AmountLimit.HasValue && drop.AmountLimit.Value >= 0) || drop.DropInStack) ?? false; } internal static void ApplyGlobalDropInStack(ref List> drops, Vector3 centerPos, float dropArea) { //IL_005f: Unknown result type (might be due to invalid IL or missing references) if (!PluginSettingsFacade.IsGlobalCharacterDropInStackEnabled() || drops.Count == 0) { return; } List> list = new List>(drops.Count); bool flag = false; foreach (KeyValuePair drop in drops) { if (!ShouldDropInStack(drop.Key, drop.Value, explicitDropInStack: false)) { list.Add(drop); continue; } SpawnStackedDrops(drop.Key, drop.Value, centerPos, dropArea); flag = true; } if (flag) { drops = list; } } internal static bool IsGlobalCharacterLootLevelScalingEnabled() { if (PluginSettingsFacade.ShouldDisableCharacterLootScalingForCreatureLevelControl()) { return false; } if (!PluginSettingsFacade.IsCharacterDropCalculateChanceLootSystemEnabled()) { return PluginSettingsFacade.IsGlobalCharacterDropTrophyLevelMultiplierEnabled(); } return true; } internal static List? SuppressGlobalCharacterLootLevelMultiplierDrops(CharacterDrop characterDrop, out IReadOnlyList? sourceDrops) { sourceDrops = null; if (!IsGlobalCharacterLootLevelScalingEnabled() || (Object)(object)characterDrop == (Object)null || characterDrop.m_drops == null || characterDrop.m_drops.Count == 0) { return null; } List list = (List)(sourceDrops = characterDrop.m_drops); List list2 = null; for (int i = 0; i < list.Count; i++) { Drop val = list[i]; if (val != null && val.m_levelMultiplier && ShouldApplyGlobalCharacterLootLevelMultiplier(val.m_prefab, val.m_levelMultiplier)) { if (list2 == null) { list2 = CloneDrops(list); } list2[i].m_levelMultiplier = false; } } if (list2 == null) { return null; } characterDrop.m_drops = list2; return list; } internal static void ApplyGlobalCharacterLootLevelMultiplier(CharacterDrop characterDrop, List> drops, IReadOnlyList? sourceDrops) { if (!IsGlobalCharacterLootLevelScalingEnabled() || (Object)(object)characterDrop == (Object)null || drops == null || drops.Count == 0) { return; } Character component = ((Component)characterDrop).GetComponent(); int num = ((!((Object)(object)component != (Object)null)) ? 1 : component.GetLevel()); if (num <= 1) { return; } bool isBoss = IsBossCharacter(component); int vanillaDropLevelFactor = GetVanillaDropLevelFactor(num); int sourceIndex = 0; for (int i = 0; i < drops.Count; i++) { KeyValuePair keyValuePair = drops[i]; Drop val = FindNextMatchingSourceDrop(sourceDrops, ref sourceIndex, keyValuePair.Key); if (ShouldApplyGlobalCharacterLootLevelMultiplier(keyValuePair.Key, val?.m_levelMultiplier ?? false) && (val == null || !val.m_onePerPlayer)) { int num2 = ScaleGlobalCharacterLootAmount(NormalizeGeneratedLevelScaledAmount(keyValuePair.Value, vanillaDropLevelFactor, val), num, isBoss); if (num2 != keyValuePair.Value) { drops[i] = new KeyValuePair(keyValuePair.Key, num2); } } } } private static Drop? FindNextMatchingSourceDrop(IReadOnlyList? sourceDrops, ref int sourceIndex, GameObject prefab) { if (sourceDrops == null || (Object)(object)prefab == (Object)null) { return null; } while (sourceIndex < sourceDrops.Count) { Drop val = sourceDrops[sourceIndex]; if (val != null && !((Object)(object)val.m_prefab != (Object)(object)prefab)) { sourceIndex++; return val; } sourceIndex++; } return null; } private static int NormalizeGeneratedLevelScaledAmount(int amount, int vanillaLevelFactor, Drop? sourceDrop) { if (amount <= 0 || vanillaLevelFactor <= 1) { return amount; } if (sourceDrop != null) { int num = Math.Max(1, Math.Max(sourceDrop.m_amountMin, sourceDrop.m_amountMax)); if (amount <= num) { return amount; } if (amount >= 100 && vanillaLevelFactor > num) { return num; } } else if (amount < vanillaLevelFactor) { return amount; } if (amount % vanillaLevelFactor != 0) { return amount; } return Math.Max(1, amount / vanillaLevelFactor); } private static int ScaleGlobalCharacterLootAmount(int amount, int characterLevel, bool isBoss) { if (amount <= 0 || characterLevel <= 1) { return amount; } int characterDropAdditionalLootChancePerStar = PluginSettingsFacade.GetCharacterDropAdditionalLootChancePerStar(isBoss); if (characterDropAdditionalLootChancePerStar <= 0) { return amount; } float num = 1f + (float)characterDropAdditionalLootChancePerStar / 100f * (float)(characterLevel - 1); float num2 = (float)amount * num; int num3 = Mathf.FloorToInt(num2); float num4 = num2 - (float)num3; if (num4 > 0f && Random.value < num4) { num3++; } return Math.Min(100, Math.Max(1, num3)); } private static bool IsBossCharacter(Character? character) { if ((Object)(object)character != (Object)null) { return character.IsBoss(); } return false; } private static int GetVanillaDropLevelFactor(int characterLevel) { return Mathf.Max(1, (int)Mathf.Pow(2f, (float)(characterLevel - 1))); } internal static List? OverrideConditionalDrops(CharacterDrop characterDrop) { CachedCharacterRuntimeDropResolution resolution; lock (Sync) { if (!TryResolveRuntimeDropResolutionLocked(characterDrop, out Character _, out resolution) || resolution == null || !resolution.HasMatchedRuntimeRule || resolution.OverrideDrops == null) { return null; } } List drops = characterDrop.m_drops; characterDrop.m_drops = resolution.OverrideDrops; return drops; } internal static bool TryHandleConfiguredDeath(CharacterDrop characterDrop) { //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: 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_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Unknown result type (might be due to invalid IL or missing references) Character character; CachedCharacterRuntimeDropResolution resolution; lock (Sync) { if (!TryResolveRuntimeDropResolutionLocked(characterDrop, out character, out resolution) || (Object)(object)character == (Object)null || resolution == null || !resolution.HasMatchedRuntimeRule) { return false; } } object obj = DropsEnabledField?.GetValue(characterDrop); if (obj is bool && !(bool)obj) { return true; } if (!resolution.HasCustomDropHandling) { return false; } List list = GenerateConfiguredDrops(character, resolution.Definitions); if (list.Count == 0) { return true; } Vector3 centerPos = character.GetCenterPoint() + ((Component)characterDrop).transform.TransformVector(characterDrop.m_spawnOffset); DropConfiguredItems(list, centerPos, 0.5f); return true; } internal static bool BeginOnePerPlayerNearbyPlayerScope(CharacterDrop characterDrop) { if (PluginSettingsFacade.GetCharacterDropOnePerPlayerNearbyRange() <= 0f) { return false; } if (OnePerPlayerScopeDepth == 0) { OnePerPlayerScopeCharacterDrop = characterDrop; } OnePerPlayerScopeDepth++; return true; } internal static void EndOnePerPlayerNearbyPlayerScope() { if (OnePerPlayerScopeDepth > 0) { OnePerPlayerScopeDepth--; if (OnePerPlayerScopeDepth == 0) { OnePerPlayerScopeCharacterDrop = null; } } } internal static bool TryGetScopedOnePerPlayerNearbyPlayerCount(out int playerCount) { playerCount = 0; float characterDropOnePerPlayerNearbyRange = PluginSettingsFacade.GetCharacterDropOnePerPlayerNearbyRange(); if (characterDropOnePerPlayerNearbyRange <= 0f || OnePerPlayerScopeDepth <= 0 || (Object)(object)OnePerPlayerScopeCharacterDrop == (Object)null) { return false; } playerCount = CountNearbyPlayers(OnePerPlayerScopeCharacterDrop, null, characterDropOnePerPlayerNearbyRange); return true; } private static IEnumerable EnumerateOverrideConfigurationPaths() { return DomainConfigurationFileSupport.EnumerateOverrideConfigurationPaths("character", PrimaryOverrideConfigurationPathYml, PrimaryOverrideConfigurationPathYaml); } private static bool IsOverrideConfigurationFileName(string fileName) { return DomainConfigurationFileSupport.IsOverrideConfigurationFileName("character", fileName); } private static void CaptureSnapshotsIfNeeded() { CharacterDropRuntime.CaptureSnapshotsIfNeeded(EnumerateRelevantPrefabs(), CaptureSnapshot); } private static void EnsureCompiledState() { if (IsGameDataReady()) { int num = ComputeGameDataSignature(); if (_compiledStateGameDataSignature != num || !string.Equals(_compiledStateConfigurationSignature, RuntimeState.ConfigurationSignature, StringComparison.Ordinal)) { _compiledState = BuildCompiledState(); _compiledStateGameDataSignature = num; _compiledStateConfigurationSignature = RuntimeState.ConfigurationSignature; } } } private static CharacterCompiledState BuildCompiledState() { CharacterCompiledState characterCompiledState = new CharacterCompiledState(); foreach (KeyValuePair> item in RuntimeState.ActiveEntriesByPrefab) { item.Deconstruct(out var key, out var value); string key2 = key; List list = value; SortedDictionary sortedDictionary = new SortedDictionary(StringComparer.Ordinal); List list2 = new List(); CharacterRuntimeDropCacheState characterRuntimeDropCacheState = new CharacterRuntimeDropCacheState(); HashSet destination = null; HashSet destination2 = null; foreach (CharacterDropPrefabEntry item2 in list) { if (item2.CharacterDrop == null) { continue; } CompiledCharacterDropRule compiledCharacterDropRule = CompileCharacterDropRule(item2); if (compiledCharacterDropRule.Drops.Count == 0) { continue; } bool flag = DropConditionEvaluator.HasCharacterConditions(item2.Conditions); CompiledCharacterDropRule compiledCharacterDropRule2 = null; foreach (CompiledCharacterDropDefinition drop in compiledCharacterDropRule.Drops) { if (!flag && !RequiresRuntimeCharacterDropHandling(drop)) { sortedDictionary.TryAdd(drop.Fingerprint, drop); continue; } if (compiledCharacterDropRule2 == null) { compiledCharacterDropRule2 = new CompiledCharacterDropRule { Entry = item2 }; } compiledCharacterDropRule2.Drops.Add(drop); } if (compiledCharacterDropRule2 != null && compiledCharacterDropRule2.Drops.Count > 0) { list2.Add(compiledCharacterDropRule2); ConditionsDefinition conditions = item2.Conditions; characterRuntimeDropCacheState.UsesLevel |= (conditions != null && conditions.Level?.HasValues() == true) || (conditions != null && conditions.MinLevel.HasValue) || (conditions?.MaxLevel.HasValue ?? false); characterRuntimeDropCacheState.UsesFaction |= HasConfiguredConditionValues(conditions?.Factions); characterRuntimeDropCacheState.UsesState |= HasConfiguredConditionValues(conditions?.States); characterRuntimeDropCacheState.UsesTimeOfDay |= conditions?.TimeOfDay != null; characterRuntimeDropCacheState.UsesRequiredEnvironments |= HasConfiguredConditionValues(conditions?.RequiredEnvironments); characterRuntimeDropCacheState.UsesInsidePlayerBase |= conditions?.InsidePlayerBase.HasValue ?? false; characterRuntimeDropCacheState.IsCacheable &= !DropConditionEvaluator.HasStaticConditions(conditions); AddNormalizedConditionValues(conditions?.RequiredGlobalKeys, ref destination); AddNormalizedConditionValues(conditions?.ForbiddenGlobalKeys, ref destination2); } } if (list2.Count > 0) { characterCompiledState.RuntimeRulesByPrefab[key2] = list2; characterRuntimeDropCacheState.RequiredGlobalKeys = destination?.ToArray() ?? Array.Empty(); characterRuntimeDropCacheState.ForbiddenGlobalKeys = destination2?.ToArray() ?? Array.Empty(); characterCompiledState.RuntimeDropCachesByPrefab[key2] = characterRuntimeDropCacheState; } if (sortedDictionary.Count > 0) { List list3 = sortedDictionary.Values.ToList(); characterCompiledState.StaticDropsByPrefab[key2] = list3; characterCompiledState.StaticBuiltDropsByPrefab[key2] = BuildDrops(list3); } } return characterCompiledState; } private static CompiledCharacterDropRule CompileCharacterDropRule(CharacterDropPrefabEntry entry) { CompiledCharacterDropRule compiledCharacterDropRule = new CompiledCharacterDropRule { Entry = entry }; IEnumerable enumerable = entry.CharacterDrop?.Drops; foreach (CharacterDropEntryDefinition item in enumerable ?? Enumerable.Empty()) { if (TryCompileCharacterDropDefinition(entry, item, out CompiledCharacterDropDefinition compiledDefinition)) { compiledCharacterDropRule.Drops.Add(compiledDefinition); } } return compiledCharacterDropRule; } private static bool TryCompileCharacterDropDefinition(CharacterDropPrefabEntry entry, CharacterDropEntryDefinition definition, out CompiledCharacterDropDefinition compiledDefinition) { compiledDefinition = null; string text = BuildCompiledDropContext(entry); string text2 = (definition.Item ?? "").Trim(); if (text2.Length == 0) { WarnInvalidEntry("Entry '" + text + "' contains a character drop without a drop prefab name."); return false; } GameObject val = ResolveDropPrefab(text2, text); if ((Object)(object)val == (Object)null) { return false; } int num = Math.Max(1, definition.AmountMin ?? 1); int amountMax = Math.Max(num, definition.AmountMax ?? definition.AmountMin ?? 1); bool effectiveCharacterDropLevelMultiplier = GetEffectiveCharacterDropLevelMultiplier(definition, val); compiledDefinition = new CompiledCharacterDropDefinition { Fingerprint = BuildDropRowFingerprint(definition, effectiveCharacterDropLevelMultiplier), Prefab = val, AmountMin = num, AmountMax = amountMax, Chance = Mathf.Max(0f, definition.Chance ?? 1f), DontScale = (definition.DontScale == true), LevelMultiplier = effectiveCharacterDropLevelMultiplier, OnePerPlayer = (definition.OnePerPlayer == true), AmountLimit = ((definition.AmountLimit.HasValue && definition.AmountLimit.Value >= 0) ? new int?(definition.AmountLimit.Value) : ((int?)null)), DropInStack = (definition.DropInStack == true) }; return true; } private static IEnumerable EnumeratePrefabs() { HashSet seen = new HashSet(); if ((Object)(object)ZNetScene.instance == (Object)null) { yield break; } foreach (GameObject prefab in ZNetScene.instance.m_prefabs) { if ((Object)(object)prefab != (Object)null && !((Object)prefab).name.StartsWith("JVLmock_", StringComparison.OrdinalIgnoreCase) && seen.Add(((Object)prefab).GetInstanceID())) { yield return prefab; } } foreach (GameObject nonNetViewPrefab in ZNetScene.instance.m_nonNetViewPrefabs) { if ((Object)(object)nonNetViewPrefab != (Object)null && !((Object)nonNetViewPrefab).name.StartsWith("JVLmock_", StringComparison.OrdinalIgnoreCase) && seen.Add(((Object)nonNetViewPrefab).GetInstanceID())) { yield return nonNetViewPrefab; } } } private static IEnumerable EnumerateRelevantPrefabs() { foreach (GameObject item in EnumeratePrefabs()) { if ((Object)(object)item != (Object)null && (Object)(object)item.GetComponent() != (Object)null) { yield return item; } } } private static int ComputeGameDataSignature() { if (!IsGameDataReady() || (Object)(object)ZNetScene.instance == (Object)null || (Object)(object)ObjectDB.instance == (Object)null) { return 0; } int frameCount = Time.frameCount; if (_cachedFrameGameDataSignatureFrame == frameCount) { return _cachedFrameGameDataSignatureValue; } int num = HashGameObjectCollection(HashGameObjectCollection((17 * 31 + ((Object)ZNetScene.instance).GetInstanceID()) * 31 + ((Object)ObjectDB.instance).GetInstanceID(), EnumerateRelevantPrefabs()), ObjectDB.instance.m_items); _cachedFrameGameDataSignatureFrame = frameCount; _cachedFrameGameDataSignatureValue = num; return num; } private static int ComputeLiveRegistrySceneSignature() { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) int num = 17; num = num * 31 + SceneManager.sceneCount; for (int i = 0; i < SceneManager.sceneCount; i++) { Scene sceneAt = SceneManager.GetSceneAt(i); num = num * 31 + ((Scene)(ref sceneAt)).handle; num = num * 31 + (((Scene)(ref sceneAt)).isLoaded ? 1 : 0); } return num; } private static int ComputeSnapshotSignature() { if (!IsGameDataReady() || (Object)(object)ZNetScene.instance == (Object)null || (Object)(object)ObjectDB.instance == (Object)null) { return 0; } return HashNamedGameObjectCollection(HashNamedGameObjectCollection(17, EnumerateRelevantPrefabs()), ObjectDB.instance.m_items); } private static int HashGameObjectCollection(int hash, IEnumerable prefabs) { foreach (GameObject prefab in prefabs) { hash = hash * 31 + (((Object)(object)prefab != (Object)null) ? ((Object)prefab).GetInstanceID() : 0); } return hash; } private static int HashNamedGameObjectCollection(int hash, IEnumerable prefabs) { List list = (from prefab in prefabs where (Object)(object)prefab != (Object)null select ((Object)prefab).name).OrderBy((string name) => name, StringComparer.Ordinal).ToList(); hash = hash * 31 + list.Count; foreach (string item in list) { hash = hash * 31 + item.GetHashCode(); } return hash; } private static CharacterDropSnapshot? CaptureSnapshot(GameObject prefab) { CharacterDrop component = prefab.GetComponent(); if ((Object)(object)component == (Object)null) { return null; } return new CharacterDropSnapshot { Prefab = prefab, Drops = CloneSnapshotDrops(component.m_drops), BuiltDrops = CloneDrops(component.m_drops, normalizeNonItemLevelMultiplier: true) }; } private static void ApplyIfReady() { if (!IsGameDataReady()) { return; } bool flag = Volatile.Read(in _synchronizedPayloadReady); if (!StandardDomainApplySupport.CanApplySynchronizedDomain(flag)) { return; } if (!CharacterDropRuntime.HasSnapshots()) { int gameDataSignature = ComputeGameDataSignature(); int snapshotSignature = ComputeSnapshotSignature(); if (CharacterDropRuntime.NeedsSnapshotBuild(snapshotSignature)) { CharacterDropRuntime.ScheduleSnapshotBuild("character apply gate", gameDataSignature, snapshotSignature, EnumerateRelevantPrefabs()); } return; } int num = ComputeGameDataSignature(); if (!CharacterDropRuntime.IsGameDataAlreadyProcessed(num)) { int snapshotSignature2 = ComputeSnapshotSignature(); CharacterDropRuntime.ScheduleSnapshotBuild("character apply gate", num, snapshotSignature2, EnumerateRelevantPrefabs()); return; } bool flag2 = ShouldApplyLocally(); Dictionary currentEntrySignatures = CloneCurrentEntrySignaturesByPrefab(); if (!StandardDomainApplySupport.IsAlreadyApplied(_lastAppliedGameDataSignature, num, _lastAppliedDomainEnabled, flag2, _lastAppliedConfigurationSignature, RuntimeState.ConfigurationSignature, _lastAppliedSynchronizedPayloadReady, flag)) { RunApplyCoordinator(num, flag2, currentEntrySignatures, RuntimeState.ConfigurationSignature); } } private static void RecordAppliedState(int gameDataSignature, bool domainEnabled, Dictionary currentEntrySignatures, string effectiveConfigurationSignature) { _lastAppliedGameDataSignature = gameDataSignature; _lastAppliedDomainEnabled = domainEnabled; _lastAppliedConfigurationSignature = effectiveConfigurationSignature; _lastAppliedSynchronizedPayloadReady = Volatile.Read(in _synchronizedPayloadReady); ReplaceEntrySignatures(_lastAppliedEntrySignaturesByPrefab, currentEntrySignatures); } private static void RestoreSnapshots(HashSet? targetPrefabs = null) { if (targetPrefabs == null) { foreach (CharacterDropSnapshot snapshot2 in CharacterDropRuntime.GetSnapshots()) { RestoreSnapshot(snapshot2); } return; } foreach (string targetPrefab in targetPrefabs) { if (CharacterDropRuntime.TryGetSnapshot(targetPrefab, out CharacterDropSnapshot snapshot) && snapshot != null) { RestoreSnapshot(snapshot); } } } private static void RestoreTrackedCharacterDrops(HashSet prefabs) { if (prefabs.Count == 0 || !IsGameDataReady() || !CharacterDropRuntime.HasSnapshots()) { return; } BootstrapRegisteredCharacterDropsIfNeeded(prefabs, forceRescan: true); foreach (CharacterDrop registeredCharacterDrop in GetRegisteredCharacterDrops()) { string prefabName = GetPrefabName(((Component)registeredCharacterDrop).gameObject); if (prefabs.Contains(prefabName) && CharacterDropRuntime.TryGetSnapshot(prefabName, out CharacterDropSnapshot snapshot) && snapshot != null) { registeredCharacterDrop.m_drops = snapshot.BuiltDrops; } } } private static void RestoreSnapshot(CharacterDropSnapshot snapshot) { CharacterDrop val = default(CharacterDrop); if (!((Object)(object)snapshot.Prefab == (Object)null) && snapshot.Prefab.TryGetComponent(ref val)) { val.m_drops = snapshot.BuiltDrops; } } private static void ValidateConfiguredPrefabs() { foreach (var (text2, list2) in RuntimeState.ActiveEntriesByPrefab) { if (CharacterDropRuntime.HasSnapshot(text2)) { continue; } foreach (CharacterDropPrefabEntry item in list2) { WarnInvalidEntry("Character prefab '" + text2 + "' from " + DescribeEntrySource(item) + " was not found in ZNetScene."); } } } private static void ApplyCurrentStateToTrackedCharacterDrop(CharacterDrop characterDrop) { if ((Object)(object)characterDrop == (Object)null || (Object)(object)((Component)characterDrop).gameObject == (Object)null || !IsGameDataReady() || !CharacterDropRuntime.HasSnapshots()) { return; } string prefabName = GetPrefabName(((Component)characterDrop).gameObject); if (!RuntimeState.PrefabsWithCharacterDropOverrides.Contains(prefabName)) { return; } int currentGameDataSignature = ComputeGameDataSignature(); bool flag = ShouldApplyLocally(); bool currentSynchronizedPayloadReady = Volatile.Read(in _synchronizedPayloadReady); if (!StandardDomainApplySupport.IsAlreadyApplied(_lastAppliedGameDataSignature, currentGameDataSignature, _lastAppliedDomainEnabled, flag, _lastAppliedConfigurationSignature, RuntimeState.ConfigurationSignature, _lastAppliedSynchronizedPayloadReady, currentSynchronizedPayloadReady) || !CharacterDropRuntime.TryGetSnapshot(prefabName, out CharacterDropSnapshot snapshot) || snapshot == null) { return; } characterDrop.m_drops = snapshot.BuiltDrops; if (flag) { EnsureCompiledState(); if (_compiledState.StaticBuiltDropsByPrefab.TryGetValue(prefabName, out List value) && value.Count != 0) { characterDrop.m_drops = value; } } } internal static void TrackCharacterDropInstance(CharacterDrop? characterDrop) { if ((Object)(object)characterDrop == (Object)null || (Object)(object)((Component)characterDrop).gameObject == (Object)null) { return; } string prefabName = GetPrefabName(((Component)characterDrop).gameObject); if (!RuntimeState.PrefabsWithCharacterDropOverrides.Contains(prefabName)) { return; } lock (Sync) { if (RuntimeState.PrefabsWithCharacterDropOverrides.Contains(prefabName)) { RegisterLiveCharacterDrop(characterDrop); ApplyCurrentStateToTrackedCharacterDrop(characterDrop); } } } internal static void UntrackCharacterDropInstance(CharacterDrop? characterDrop) { lock (Sync) { if (!((Object)(object)characterDrop == (Object)null) && CharacterDropRuntime.TryGetRegisteredPrefabName(characterDrop, out string prefabName)) { UnregisterLiveCharacterDrop(characterDrop, prefabName); } } } private static void BootstrapRegisteredCharacterDropsIfNeeded(HashSet? additionalPrefabs = null, bool forceRescan = false) { if (RuntimeState.PrefabsWithCharacterDropOverrides.Count != 0 || (additionalPrefabs != null && additionalPrefabs.Count != 0)) { CharacterDropRuntime.BootstrapRegisteredCharacterDropsIfNeeded(ComputeLiveRegistrySceneSignature(), Object.FindObjectsByType((FindObjectsSortMode)0), (CharacterDrop characterDrop) => GetPrefabName(((Component)characterDrop).gameObject), ShouldRegisterBootstrappedCharacterDropPrefab, additionalPrefabs, forceRescan); } } private static IEnumerable GetRegisteredCharacterDrops() { return CharacterDropRuntime.GetRegisteredCharacterDrops(); } private static Dictionary CloneCurrentEntrySignaturesByPrefab() { return new Dictionary(RuntimeState.CurrentEntrySignaturesByPrefab, StringComparer.OrdinalIgnoreCase); } private static Dictionary BuildActiveEntrySignaturesByPrefab(Dictionary> activeEntriesByPrefab) { return DomainEntrySignatureSupport.BuildSignaturesByKey(activeEntriesByPrefab, (List entries) => NetworkPayloadSyncSupport.ComputeCharacterConfigurationSignature(entries.OrderBy((CharacterDropPrefabEntry entry) => entry.RuleId, StringComparer.Ordinal).ToList())); } private static bool TryBuildEffectiveRuntimeDropDefinitions(IEnumerable? staticDefinitions, IEnumerable runtimeRules, Character character, out List definitions) { SortedDictionary sortedDictionary = new SortedDictionary(StringComparer.Ordinal); foreach (CompiledCharacterDropDefinition item in staticDefinitions ?? Enumerable.Empty()) { sortedDictionary.TryAdd(item.Fingerprint, item); } bool result = false; foreach (CompiledCharacterDropRule item2 in runtimeRules ?? Enumerable.Empty()) { if (!EntryMatchesCharacter(item2.Entry, character)) { continue; } result = true; foreach (CompiledCharacterDropDefinition drop in item2.Drops) { sortedDictionary.TryAdd(drop.Fingerprint, drop); } } definitions = sortedDictionary.Values.ToList(); return result; } private static bool TryResolveRuntimeDropResolutionLocked(CharacterDrop characterDrop, out Character? character, out CachedCharacterRuntimeDropResolution? resolution) { resolution = null; character = null; if (!CanUseCurrentRuntimeState()) { return false; } string prefabName = GetPrefabName(((Component)characterDrop).gameObject); EnsureCompiledState(); if (!_compiledState.RuntimeRulesByPrefab.TryGetValue(prefabName, out List value) || value.Count == 0) { return false; } character = ((Component)characterDrop).GetComponent(); if ((Object)(object)character == (Object)null) { return false; } CharacterRuntimeDropCacheState value2; CharacterRuntimeDropCacheState characterRuntimeDropCacheState = (_compiledState.RuntimeDropCachesByPrefab.TryGetValue(prefabName, out value2) ? value2 : null); if (characterRuntimeDropCacheState != null && characterRuntimeDropCacheState.IsCacheable) { int num = ComputeCachedRuntimeDropSignature(character, characterRuntimeDropCacheState); if (characterRuntimeDropCacheState.ResolutionsBySignature.TryGetValue(num, out resolution)) { return resolution.HasMatchedRuntimeRule; } resolution = BuildRuntimeDropResolution(_compiledState.StaticDropsByPrefab.TryGetValue(prefabName, out List value3) ? value3 : null, value, character); StoreCachedRuntimeDropResolution(characterRuntimeDropCacheState, num, resolution); return resolution.HasMatchedRuntimeRule; } resolution = BuildRuntimeDropResolution(_compiledState.StaticDropsByPrefab.TryGetValue(prefabName, out List value4) ? value4 : null, value, character); return resolution.HasMatchedRuntimeRule; } private static CachedCharacterRuntimeDropResolution BuildRuntimeDropResolution(IReadOnlyList? staticDefinitions, IReadOnlyList runtimeRules, Character character) { if (!TryBuildEffectiveRuntimeDropDefinitions(staticDefinitions, runtimeRules, character, out List definitions)) { return new CachedCharacterRuntimeDropResolution { HasMatchedRuntimeRule = false }; } CompiledCharacterDropDefinition[] array = definitions.ToArray(); return new CachedCharacterRuntimeDropResolution { HasMatchedRuntimeRule = true, Definitions = array, OverrideDrops = BuildDrops(array), HasCustomDropHandling = HasCustomDropHandling(array) }; } private static void StoreCachedRuntimeDropResolution(CharacterRuntimeDropCacheState cacheState, int runtimeSignature, CachedCharacterRuntimeDropResolution resolution) { if (cacheState.ResolutionsBySignature.Count >= 32 && !cacheState.ResolutionsBySignature.ContainsKey(runtimeSignature)) { cacheState.ResolutionsBySignature.Clear(); } cacheState.ResolutionsBySignature[runtimeSignature] = resolution; } private static int ComputeCachedRuntimeDropSignature(Character character, CharacterRuntimeDropCacheState cacheState) { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Expected I4, but got Unknown //IL_0133: Unknown result type (might be due to invalid IL or missing references) int num = 17; if (cacheState.UsesLevel) { num = CombineCachedRuntimeDropSignature(num, character.GetLevel()); } if (cacheState.UsesFaction) { num = CombineCachedRuntimeDropSignature(num, (int)character.GetFaction()); } if (cacheState.UsesState) { num = CombineCachedRuntimeDropSignature(num, character.IsTamed()); MonsterAI component = ((Component)character).GetComponent(); num = CombineCachedRuntimeDropSignature(num, (Object)(object)component != (Object)null && component.IsEventCreature()); } if (cacheState.UsesTimeOfDay || cacheState.UsesRequiredEnvironments || cacheState.RequiredGlobalKeys.Length != 0 || cacheState.ForbiddenGlobalKeys.Length != 0) { CharacterRuntimeContextSnapshot orCreateRuntimeContextSnapshot = GetOrCreateRuntimeContextSnapshot(); if (cacheState.UsesTimeOfDay) { num = CombineCachedRuntimeDropSignature(num, orCreateRuntimeContextSnapshot.TimeOfDayPhaseMarker); } if (cacheState.UsesRequiredEnvironments) { num = CombineCachedRuntimeDropSignature(num, orCreateRuntimeContextSnapshot.EnvironmentName); } string[] requiredGlobalKeys = cacheState.RequiredGlobalKeys; foreach (string text in requiredGlobalKeys) { num = CombineCachedRuntimeDropSignature(num, text); num = CombineCachedRuntimeDropSignature(num, GetCachedRuntimeDropGlobalKeyState(orCreateRuntimeContextSnapshot, text)); } requiredGlobalKeys = cacheState.ForbiddenGlobalKeys; foreach (string text2 in requiredGlobalKeys) { num = CombineCachedRuntimeDropSignature(num, text2); num = CombineCachedRuntimeDropSignature(num, GetCachedRuntimeDropGlobalKeyState(orCreateRuntimeContextSnapshot, text2)); } } if (cacheState.UsesInsidePlayerBase) { bool value = (Object)(object)EffectArea.IsPointInsideArea(((Component)character).transform.position, (Type)4, 0f) != (Object)null; num = CombineCachedRuntimeDropSignature(num, value); } return num; } private static CharacterRuntimeContextSnapshot GetOrCreateRuntimeContextSnapshot() { int frameCount = Time.frameCount; if (_runtimeContextSnapshot != null && _runtimeContextSnapshot.Frame == frameCount) { return _runtimeContextSnapshot; } CharacterRuntimeContextSnapshot obj = new CharacterRuntimeContextSnapshot { Frame = frameCount, TimeOfDayPhaseMarker = TimeOfDayFormatting.GetCurrentRuntimePhaseMarker() }; EnvMan instance = EnvMan.instance; obj.EnvironmentName = ((instance == null) ? null : instance.GetCurrentEnvironment()?.m_name) ?? ""; _runtimeContextSnapshot = obj; return _runtimeContextSnapshot; } private static bool GetCachedRuntimeDropGlobalKeyState(CharacterRuntimeContextSnapshot runtimeContext, string key) { if (runtimeContext.GlobalKeyStates.TryGetValue(key, out var value)) { return value; } value = (Object)(object)ZoneSystem.instance != (Object)null && ZoneSystem.instance.GetGlobalKey(key); runtimeContext.GlobalKeyStates[key] = value; return value; } private static int CombineCachedRuntimeDropSignature(int current, bool value) { return current * 31 + (value ? 1 : 0); } private static int CombineCachedRuntimeDropSignature(int current, int value) { return current * 31 + value; } private static int CombineCachedRuntimeDropSignature(int current, string value) { return current * 31 + (value?.GetHashCode() ?? 0); } private static bool HasConfiguredConditionValues(IEnumerable? values) { if (values == null) { return false; } foreach (string value in values) { if (!string.IsNullOrWhiteSpace(value)) { return true; } } return false; } private static void AddNormalizedConditionValues(IEnumerable? values, ref HashSet? destination) { if (values == null) { return; } foreach (string value in values) { string text = (value ?? "").Trim(); if (text.Length != 0) { if (destination == null) { destination = new HashSet(StringComparer.Ordinal); } destination.Add(text); } } } private static bool RequiresRuntimeCharacterDropHandling(CompiledCharacterDropDefinition definition) { if (!definition.AmountLimit.HasValue) { return definition.DropInStack; } return true; } private static bool EntryMatchesCharacter(CharacterDropPrefabEntry? entry, Character character) { if (entry != null) { return DropConditionEvaluator.AreSatisfied(character, entry.Conditions); } return false; } private static string BuildDropRowFingerprint(CharacterDropEntryDefinition definition, bool levelMultiplier) { return NetworkPayloadSyncSupport.ComputeCharacterDropRowSignature(new CharacterDropEntryDefinition { Item = (definition.Item ?? "").Trim(), AmountMin = Math.Max(1, definition.AmountMin ?? 1), AmountMax = Math.Max(Math.Max(1, definition.AmountMin ?? 1), definition.AmountMax ?? definition.AmountMin ?? 1), Chance = Mathf.Max(0f, definition.Chance ?? 1f), OnePerPlayer = (definition.OnePerPlayer == true), LevelMultiplier = levelMultiplier, DontScale = (definition.DontScale == true), AmountLimit = ((definition.AmountLimit.HasValue && definition.AmountLimit.Value >= 0) ? new int?(definition.AmountLimit.Value) : ((int?)null)), DropInStack = (definition.DropInStack == true) }); } private static bool HasAddedPrefabs(Dictionary previous, Dictionary current) { foreach (string key in current.Keys) { if (!previous.ContainsKey(key)) { return true; } } return false; } private static HashSet BuildDirtyPrefabs(Dictionary previous, Dictionary current) { return DomainDictionaryDiffSupport.BuildDirtyKeys(previous, current); } private static void ReplaceEntrySignatures(Dictionary target, Dictionary source) { DomainDictionaryDiffSupport.ReplaceEntries(target, source); } private static void RegisterLiveCharacterDrop(CharacterDrop characterDrop) { if (!((Object)(object)characterDrop == (Object)null) && !((Object)(object)((Component)characterDrop).gameObject == (Object)null)) { string prefabName = GetPrefabName(((Component)characterDrop).gameObject); if (prefabName.Length != 0) { CharacterDropRuntime.RegisterLiveCharacterDrop(characterDrop, prefabName); } } } private static bool ShouldRegisterBootstrappedCharacterDropPrefab(string prefabName) { if (prefabName.Length > 0) { return RuntimeState.PrefabsWithCharacterDropOverrides.Contains(prefabName); } return false; } private static List GetOrCreateActiveEntries(Dictionary> activeEntriesByPrefab, string prefabName) { if (!activeEntriesByPrefab.TryGetValue(prefabName, out List value)) { value = (activeEntriesByPrefab[prefabName] = new List()); } return value; } private static IEnumerable GetRegisteredCharacterDrops(HashSet dirtyPrefabs) { return CharacterDropRuntime.GetRegisteredCharacterDrops(dirtyPrefabs); } private static void UnregisterLiveCharacterDrop(CharacterDrop characterDrop, string prefabName) { CharacterDropRuntime.UnregisterLiveCharacterDrop(characterDrop, prefabName); } private static List CloneSnapshotDrops(List drops) { List list = new List(drops.Count); foreach (Drop drop in drops) { list.Add(new CharacterDropItemSnapshot { ItemPrefab = drop.m_prefab, AmountMin = drop.m_amountMin, AmountMax = drop.m_amountMax, Chance = drop.m_chance, OnePerPlayer = drop.m_onePerPlayer, LevelMultiplier = NormalizeBaselineCharacterDropLevelMultiplier(drop.m_prefab, drop.m_levelMultiplier), DontScale = drop.m_dontScale }); } return list; } private static List CloneDrops(List drops, bool normalizeNonItemLevelMultiplier = false) { //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_002f: 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_0047: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_005f: 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_0092: Expected O, but got Unknown List list = new List(drops.Count); foreach (Drop drop in drops) { list.Add(new Drop { m_prefab = drop.m_prefab, m_amountMin = drop.m_amountMin, m_amountMax = drop.m_amountMax, m_chance = drop.m_chance, m_onePerPlayer = drop.m_onePerPlayer, m_levelMultiplier = (normalizeNonItemLevelMultiplier ? NormalizeBaselineCharacterDropLevelMultiplier(drop.m_prefab, drop.m_levelMultiplier) : drop.m_levelMultiplier), m_dontScale = drop.m_dontScale }); } return list; } private static List BuildDrops(IReadOnlyList definitions) { //IL_001d: 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_002e: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_0052: 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_006a: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Expected O, but got Unknown List list = new List(definitions.Count); foreach (CompiledCharacterDropDefinition definition in definitions) { list.Add(new Drop { m_prefab = definition.Prefab, m_amountMin = definition.AmountMin, m_amountMax = definition.AmountMax, m_chance = definition.Chance, m_onePerPlayer = definition.OnePerPlayer, m_levelMultiplier = definition.LevelMultiplier, m_dontScale = definition.DontScale }); } return list; } private static List GenerateConfiguredDrops(Character character, IReadOnlyList definitions) { List list = new List(); int level = character.GetLevel(); int vanillaDropLevelFactor = GetVanillaDropLevelFactor(level); int onePerPlayerDropCount = GetOnePerPlayerDropCount(character); bool isBoss = IsBossCharacter(character); foreach (CompiledCharacterDropDefinition definition in definitions) { float num = definition.Chance; bool flag = ShouldApplyGlobalCharacterLootLevelMultiplier(definition.Prefab, definition.LevelMultiplier); bool flag2 = definition.LevelMultiplier && !flag; if (flag2) { num *= (float)vanillaDropLevelFactor; } if (!(Random.value > num)) { int num2 = (definition.DontScale ? Random.Range(definition.AmountMin, definition.AmountMax) : RollConfiguredDropAmount(definition.Prefab, definition.AmountMin, definition.AmountMax)); if (flag2) { num2 *= vanillaDropLevelFactor; } if (definition.OnePerPlayer) { num2 = onePerPlayerDropCount; } if (flag && !definition.OnePerPlayer) { num2 = ScaleGlobalCharacterLootAmount(num2, level, isBoss); } num2 = Math.Min(num2, 100); if (definition.AmountLimit.HasValue) { num2 = Math.Min(num2, definition.AmountLimit.Value); } if (num2 > 0) { list.Add(new ResolvedConfiguredDrop { Prefab = definition.Prefab, Amount = num2, DropInStack = definition.DropInStack }); } } } return list; } private static int GetOnePerPlayerDropCount(Character character) { float characterDropOnePerPlayerNearbyRange = PluginSettingsFacade.GetCharacterDropOnePerPlayerNearbyRange(); CharacterDrop characterDrop = default(CharacterDrop); if (characterDropOnePerPlayerNearbyRange > 0f && ((Component)character).TryGetComponent(ref characterDrop)) { return CountNearbyPlayers(characterDrop, character, characterDropOnePerPlayerNearbyRange); } if (!((Object)(object)ZNet.instance != (Object)null)) { return 1; } return ZNet.instance.GetNrOfPlayers(); } private static int CountNearbyPlayers(CharacterDrop characterDrop, Character? character, float range) { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) if (character == null) { character = ((Component)characterDrop).GetComponent(); } return SceneProximityQueries.CountPlayersInRangeXZ(((Object)(object)character != (Object)null) ? character.GetCenterPoint() : ((Component)characterDrop).transform.position, range, livingPlayersOnly: true); } private static void DropConfiguredItems(List drops, Vector3 centerPos, float dropArea) { //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) List> list = new List>(); foreach (ResolvedConfiguredDrop drop in drops) { if (!ShouldDropInStack(drop.Prefab, drop.Amount, drop.DropInStack)) { list.Add(new KeyValuePair(drop.Prefab, drop.Amount)); } else { SpawnStackedDrops(drop.Prefab, drop.Amount, centerPos, dropArea); } } if (list.Count > 0) { SpawnConfiguredLooseDrops(list, centerPos, dropArea); } } private static bool GetEffectiveCharacterDropLevelMultiplier(CharacterDropEntryDefinition definition, GameObject? dropPrefab) { return definition.LevelMultiplier ?? GetDefaultCharacterDropLevelMultiplier(dropPrefab); } private static bool GetConfiguredCharacterDropLevelMultiplierForOutput(CharacterDropEntryDefinition definition) { return definition.LevelMultiplier ?? GetDefaultCharacterDropLevelMultiplier(ResolveKnownPrefab(definition.Item)); } private static bool GetDefaultCharacterDropLevelMultiplier(GameObject? dropPrefab) { return IsItemDropPrefab(dropPrefab); } private static bool NormalizeBaselineCharacterDropLevelMultiplier(GameObject? dropPrefab, bool levelMultiplier) { return IsItemDropPrefab(dropPrefab) && levelMultiplier; } private static bool ShouldApplyGlobalCharacterLootLevelMultiplier(GameObject? itemPrefab, bool levelMultiplier) { if (!IsItemDropPrefab(itemPrefab)) { return false; } if (IsTrophyItemPrefab(itemPrefab)) { return ShouldApplyGlobalTrophyLevelMultiplier(itemPrefab); } if (levelMultiplier) { return PluginSettingsFacade.IsCharacterDropCalculateChanceLootSystemEnabled(); } return false; } private static bool ShouldApplyGlobalTrophyLevelMultiplier(GameObject? itemPrefab) { if (PluginSettingsFacade.IsGlobalCharacterDropTrophyLevelMultiplierEnabled() && (Object)(object)itemPrefab != (Object)null && !PluginSettingsFacade.IsCharacterDropTrophyLevelMultiplierBlacklisted(((Object)itemPrefab).name)) { return IsTrophyItemPrefab(itemPrefab); } return false; } private static bool IsTrophyItemPrefab(GameObject itemPrefab) { //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Invalid comparison between Unknown and I4 ItemDrop val = default(ItemDrop); if (!itemPrefab.TryGetComponent(ref val)) { return false; } SharedData val2 = val.m_itemData?.m_shared; if (val2 != null) { return (int)val2.m_itemType == 13; } return false; } private static bool IsItemDropPrefab(GameObject? prefab) { ItemDrop itemDrop; return IsItemDropPrefab(prefab, out itemDrop); } private static bool IsItemDropPrefab(GameObject? prefab, out ItemDrop? itemDrop) { itemDrop = null; if ((Object)(object)prefab != (Object)null) { return prefab.TryGetComponent(ref itemDrop); } return false; } private static bool ShouldDropInStack(GameObject prefab, int amount, bool explicitDropInStack) { ItemDrop val = default(ItemDrop); if ((Object)(object)prefab == (Object)null || !prefab.TryGetComponent(ref val) || val.m_itemData.m_shared.m_maxStackSize <= 1 || PluginSettingsFacade.IsCharacterDropInStackBlacklisted(((Object)prefab).name)) { return false; } if (!explicitDropInStack) { if (PluginSettingsFacade.IsGlobalCharacterDropInStackEnabled()) { return amount > 1; } return false; } return true; } private static void SpawnStackedDrops(GameObject prefab, int amount, Vector3 centerPos, float dropArea) { //IL_0034: Unknown result type (might be due to invalid IL or missing references) ItemDrop val = default(ItemDrop); if (amount > 0 && prefab.TryGetComponent(ref val)) { int num = amount; int val2 = Math.Max(1, val.m_itemData.m_shared.m_maxStackSize); while (num > 0) { int num2 = Math.Min(num, val2); SpawnStackedItem(prefab, num2, centerPos, dropArea); num -= num2; } } } private static void SpawnStackedItem(GameObject prefab, int stackSize, Vector3 centerPos, float dropArea) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0064: 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_0089: 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) Quaternion val = Quaternion.Euler(0f, (float)Random.Range(0, 360), 0f); Vector3 val2 = ResolveConfiguredDropSpawnPoint(centerPos, dropArea); GameObject obj = Object.Instantiate(prefab, val2, val); ItemDrop val3 = default(ItemDrop); if (obj.TryGetComponent(ref val3)) { ItemDrop.OnCreateNew(val3); val3.SetStack(stackSize); val3.m_itemData.m_worldLevel = (byte)Game.m_worldLevel; } Rigidbody val4 = default(Rigidbody); if (obj.TryGetComponent(ref val4)) { Vector3 insideUnitSphere = Random.insideUnitSphere; if (insideUnitSphere.y < 0f) { insideUnitSphere.y = 0f - insideUnitSphere.y; } val4.AddForce(insideUnitSphere * 5f, (ForceMode)2); } } private static void SpawnConfiguredLooseDrops(List> drops, Vector3 centerPos, float dropArea) { //IL_001c: Unknown result type (might be due to invalid IL or missing references) foreach (KeyValuePair drop in drops) { for (int i = 0; i < drop.Value; i++) { SpawnConfiguredLooseItem(drop.Key, centerPos, dropArea); } } } private static void SpawnConfiguredLooseItem(GameObject prefab, Vector3 centerPos, float dropArea) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_0026: 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_0055: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_0075: 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_0067: Unknown result type (might be due to invalid IL or missing references) Quaternion val = Quaternion.Euler(0f, (float)Random.Range(0, 360), 0f); Vector3 val2 = ResolveConfiguredDropSpawnPoint(centerPos, dropArea); GameObject obj = Object.Instantiate(prefab, val2, val); ItemDrop val3 = default(ItemDrop); if (obj.TryGetComponent(ref val3)) { val3.m_itemData.m_worldLevel = (byte)Game.m_worldLevel; } Rigidbody val4 = default(Rigidbody); if (obj.TryGetComponent(ref val4)) { Vector3 insideUnitSphere = Random.insideUnitSphere; if (insideUnitSphere.y < 0f) { insideUnitSphere.y = 0f - insideUnitSphere.y; } val4.AddForce(insideUnitSphere * 5f, (ForceMode)2); } } private static Vector3 ResolveConfiguredDropSpawnPoint(Vector3 centerPos, float dropArea) { //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_000b: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) Vector2 val = Random.insideUnitCircle * dropArea; Vector3 point = centerPos + new Vector3(val.x, 0f, val.y); TrySnapConfiguredDropSpawnPointToGround(ref point); return point; } private static bool TrySnapConfiguredDropSpawnPointToGround(ref Vector3 point) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) float num = default(float); if ((Object)(object)ZoneSystem.instance != (Object)null && ZoneSystem.instance.GetSolidHeight(point, ref num, 1000)) { point.y = num + 0.2f; return true; } if (WorldGenerator.instance != null) { point.y = WorldGenerator.instance.GetHeight(point.x, point.z) + 0.2f; return true; } return false; } private static int RollConfiguredDropAmount(GameObject prefab, int amountMin, int amountMax) { ItemDrop val = default(ItemDrop); if (!((Object)(object)prefab != (Object)null) || !prefab.TryGetComponent(ref val)) { return Random.Range(amountMin, amountMax); } return Game.instance.ScaleDrops(prefab, amountMin, amountMax); } private static GameObject? ResolveDropPrefab(string prefabName, string context) { string text = (prefabName ?? "").Trim(); if (text.Length == 0) { WarnInvalidEntry("Entry '" + context + "' references an empty drop prefab name."); return null; } GameObject val = ResolveKnownPrefab(text); if ((Object)(object)val == (Object)null) { WarnInvalidEntry("Entry '" + context + "' references unknown drop prefab '" + text + "'."); return null; } return val; } private static GameObject? ResolveKnownPrefab(string prefabName) { string text = (prefabName ?? "").Trim(); object obj; if (text.Length != 0) { ObjectDB instance = ObjectDB.instance; obj = ((instance != null) ? instance.GetItemPrefab(text) : null); if (obj == null) { ZNetScene instance2 = ZNetScene.instance; if (instance2 == null) { return null; } return instance2.GetPrefab(text); } } else { obj = null; } return (GameObject?)obj; } private 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; } string name = ((Object)gameObject).name; if (name.EndsWith("(Clone)", StringComparison.Ordinal)) { string text = name; int length = "(Clone)".Length; return text.Substring(0, text.Length - length).TrimEnd(); } return name; } private static void WarnInvalidEntry(string message) { InvalidEntryWarnings.Warn(message); } private static void LogPartiallyAcceptedLocalConfiguration(int totalEntries, int acceptedEntries, IEnumerable warnings) { int num = Math.Max(0, totalEntries - acceptedEntries); DropNSpawnPlugin.DropNSpawnLogger.LogWarning((object)("Skipped " + num.ToString(CultureInfo.InvariantCulture) + " invalid character entr" + ((num == 1) ? "y" : "ies") + " and kept " + acceptedEntries.ToString(CultureInfo.InvariantCulture) + " valid entr" + ((acceptedEntries == 1) ? "y" : "ies") + ".")); foreach (string item in warnings.Where((string message) => !string.IsNullOrWhiteSpace(message)).Distinct(StringComparer.OrdinalIgnoreCase)) { DropNSpawnPlugin.DropNSpawnLogger.LogWarning((object)item); } } private static void LogLocalConfigurationLoaded(int acceptedEntryCount, int loadedFileCount) { DropNSpawnPlugin.DropNSpawnLogger.LogInfo((object)$"Loaded {acceptedEntryCount} character drop configuration(s) from {loadedFileCount} override file(s)."); } private static void OnSourceOfTruthPayloadUnchanged() { if (!NetworkPayloadSyncSupport.IsPayloadCurrent(Descriptor, RuntimeState.ConfigurationSignature)) { ConfigurationDomainHost.PublishSyncedPayload(DropNSpawnPlugin.IsSourceOfTruth, Descriptor, RuntimeState.Configuration, RuntimeState.ConfigurationSignature); } } private static void LogSyncedCharacterConfigurationLoaded(string payloadToken, int acceptedEntryCount) { DropNSpawnPlugin.DropNSpawnLogger.LogInfo((object)$"Loaded {acceptedEntryCount} synchronized character configuration(s) from the server."); } private static void LogSyncedCharacterConfigurationFailure(string payloadToken, Exception ex) { DropNSpawnPlugin.DropNSpawnLogger.LogError((object)$"Failed to deserialize synchronized character payload DTO. {ex}"); } private static InvalidEntryDiagnostics.SuppressionScope BeginInvalidEntryWarningSuppressionForSyncedClientBuild(string sourceName) { return InvalidEntryWarnings.BeginSuppressionForSyncedClientBuild(sourceName); } private static string CreateConfigurationContext(CharacterDropPrefabEntry entry) { return (string.IsNullOrWhiteSpace(entry.Prefab) ? "" : entry.Prefab) + " @ " + DescribeEntrySource(entry); } private static string DescribeEntrySource(CharacterDropPrefabEntry entry) { string text = DescribeEntrySource(entry.SourcePath); if (entry.SourceLine > 0) { text = text + ":" + entry.SourceLine.ToString(CultureInfo.InvariantCulture); } return text; } private static string DescribeEntrySource(string? sourcePath) { string text = sourcePath ?? ""; if (text.Length == 0) { return "unknown source"; } text.StartsWith("ServerSync:", StringComparison.Ordinal); return text; } private static string FormatYamlExceptionLocation(Exception ex) { if (!(ex is YamlException { Start: var start })) { return ""; } if (start.Line <= 0) { return ""; } return " at line " + start.Line.ToString(CultureInfo.InvariantCulture); } private static string BuildCompiledDropContext(CharacterDropPrefabEntry entry) { string text = (string.IsNullOrWhiteSpace(entry.Prefab) ? "" : entry.Prefab); string text2 = (string.IsNullOrWhiteSpace(entry.RuleId) ? "" : entry.RuleId); return text + "/" + text2 + "@" + DescribeEntrySource(entry); } private static void RunApplyCoordinator(int gameDataSignature, bool domainEnabled, Dictionary currentEntrySignatures, string effectiveConfigurationSignature) { CharacterDesiredState characterDesiredState = BuildCharacterDesiredState(gameDataSignature, domainEnabled, currentEntrySignatures, effectiveConfigurationSignature); StandardBaselineDesiredStateCoordinator.Run(characterDesiredState.ApplyPlan, characterDesiredState, CharacterApplyOperations.Instance); } private static CharacterDesiredState BuildCharacterDesiredState(int gameDataSignature, bool domainEnabled, Dictionary currentEntrySignatures, string effectiveConfigurationSignature) { bool flag = !string.Equals(_lastAppliedConfigurationSignature, effectiveConfigurationSignature, StringComparison.Ordinal); bool canUseTargetedLiveReload = _lastAppliedGameDataSignature == gameDataSignature && _lastAppliedDomainEnabled == true && !flag && !HasAddedPrefabs(_lastAppliedEntrySignaturesByPrefab, currentEntrySignatures); EnsureCompiledState(); return new CharacterDesiredState { GameDataSignature = gameDataSignature, EffectiveConfigurationSignature = effectiveConfigurationSignature, ApplyPlan = StandardDomainApplySupport.BuildPlan(_lastAppliedGameDataSignature, gameDataSignature, _lastAppliedDomainEnabled, domainEnabled, _lastAppliedEntrySignaturesByPrefab, currentEntrySignatures, EmptyEntrySignatures, canUseTargetedLiveReload), CurrentEntrySignatures = currentEntrySignatures, CompiledState = _compiledState, DomainEnabled = domainEnabled }; } private static void RestoreCharacterStaticBaseline(CharacterDesiredState desiredState) { RestoreSnapshots(); } private static void ValidateCharacterDesiredState(CharacterDesiredState desiredState) { ValidateConfiguredPrefabs(); } private static void ApplyCharacterDesiredStateToStaticBaseline(CharacterDesiredState desiredState) { if (!desiredState.DomainEnabled) { return; } IEnumerable enumerable; if (desiredState.ApplyPlan.DirtyKeys == null) { IEnumerable keys = desiredState.CompiledState.StaticDropsByPrefab.Keys; enumerable = keys; } else { IEnumerable keys = desiredState.ApplyPlan.DirtyKeys; enumerable = keys; } CharacterDrop val = default(CharacterDrop); foreach (string item in enumerable) { if (desiredState.CompiledState.StaticBuiltDropsByPrefab.TryGetValue(item, out List value) && value.Count != 0 && CharacterDropRuntime.TryGetSnapshot(item, out CharacterDropSnapshot snapshot) && snapshot != null && !((Object)(object)snapshot.Prefab == (Object)null) && snapshot.Prefab.TryGetComponent(ref val)) { val.m_drops = value; } } } private static void ApplyCharacterDesiredStateToLive(CharacterDesiredState desiredState) { BootstrapRegisteredCharacterDropsIfNeeded(desiredState.ApplyPlan.DirtyKeys, desiredState.ApplyPlan.DirtyKeys != null); foreach (CharacterDrop item in (desiredState.ApplyPlan.DirtyKeys == null) ? GetRegisteredCharacterDrops() : GetRegisteredCharacterDrops(desiredState.ApplyPlan.DirtyKeys)) { if ((Object)(object)item == (Object)null || (Object)(object)((Component)item).gameObject == (Object)null) { continue; } RegisterLiveCharacterDrop(item); string prefabName = GetPrefabName(((Component)item).gameObject); if (CharacterDropRuntime.TryGetSnapshot(prefabName, out CharacterDropSnapshot snapshot) && snapshot != null) { item.m_drops = snapshot.BuiltDrops; if (desiredState.DomainEnabled && desiredState.CompiledState.StaticBuiltDropsByPrefab.TryGetValue(prefabName, out List value) && value.Count != 0) { item.m_drops = value; } } } } private static List> BuildConfigurationTemplate() { return PrefabOutputSections.BuildSections(CharacterDropRuntime.GetSnapshots().Select(BuildConfigurationEntry), (CharacterDropPrefabEntry entry) => entry.Prefab); } private static string BuildReferenceConfigurationTemplate() { return PrefabOutputSections.SerializeReferenceSections((from section in BuildConfigurationTemplate() select new PrefabOwnerSection(section.OwnerName, section.Entries.Select((CharacterDropPrefabEntry entry) => new CharacterDropReferenceEntry { Prefab = entry.Prefab, CharacterDrop = entry.CharacterDrop }).ToList())).ToList(), Serializer); } internal static bool TryWriteFullScaffoldConfigurationFile(out string path, out string error) { string content; string logMessage; lock (Sync) { path = FullScaffoldConfigurationPath; error = ""; if (!IsGameDataReady() && !CharacterDropRuntime.HasSnapshots()) { error = "Character game data is not ready yet."; return false; } CaptureSnapshotsIfNeeded(); content = BuildFullScaffoldConfigurationTemplate(); logMessage = "Wrote character full scaffold configuration to " + path + "."; } GeneratedArtifactWriter.WriteTextAlways(path, content, logMessage); return true; } internal static void RefreshReferenceConfigurationFile() { string content; string sourceSignature; string logMessage; lock (Sync) { if (!IsGameDataReady()) { return; } CaptureSnapshotsIfNeeded(); content = BuildReferenceConfigurationTemplate(); sourceSignature = ComputeReferenceSourceSignature(); logMessage = "Updated character reference configuration at " + ReferenceConfigurationPath + "."; } WriteReferenceConfigurationFile(content, logMessage); ReferenceArtifactLifecycle.RecordUpdate("character", ReferenceConfigurationPath, sourceSignature); } private static void WriteReferenceConfigurationFile(string content, string logMessage) { GeneratedArtifactWriter.WriteText(ReferenceConfigurationPath, content, logMessage); } private static void EnsureReferenceArtifactsUpToDate() { if (IsGameDataReady()) { string sourceSignature = ComputeReferenceSourceSignature(); if (ReferenceArtifactLifecycle.TryPlanUpdate("character", ReferenceConfigurationPath, sourceSignature, out var updateKind)) { CaptureSnapshotsIfNeeded(); WriteReferenceConfigurationFile(BuildReferenceConfigurationTemplate(), ReferenceArtifactLifecycle.FormatAction(updateKind) + " character reference configuration at " + ReferenceConfigurationPath + "."); ReferenceArtifactLifecycle.RecordUpdate("character", ReferenceConfigurationPath, sourceSignature); } } } private static string ComputeReferenceSourceSignature() { return ReferenceRefreshSupport.ComputeStableHashForKeys(from prefab in EnumerateRelevantPrefabs() select ((Object)prefab).name); } private static string FormatYamlBool(bool value) { if (!value) { return "false"; } return "true"; } private static string FormatYamlFloat(float value) { return value.ToString("0.###", CultureInfo.InvariantCulture); } private static string FormatYamlString(string value) { if (value.Length == 0) { return "''"; } if (!char.IsWhiteSpace(value[0]) && !char.IsWhiteSpace(value[value.Length - 1]) && value.IndexOfAny(new char[17] { ':', '#', '{', '}', '[', ']', ',', '\'', '"', '&', '*', '!', '|', '>', '%', '@', '`' }) < 0 && value[0] != '-' && value[0] != '?' && !string.Equals(value, "null", StringComparison.OrdinalIgnoreCase) && !string.Equals(value, "true", StringComparison.OrdinalIgnoreCase) && !string.Equals(value, "false", StringComparison.OrdinalIgnoreCase)) { return value; } return "'" + value.Replace("'", "''") + "'"; } private static CharacterDropPrefabEntry BuildConfigurationEntry(CharacterDropSnapshot snapshot) { List list = (from drop in snapshot.Drops select new { Name = NormalizeReferenceItemName(drop.ItemPrefab), Drop = drop } into entry where !string.IsNullOrWhiteSpace(entry.Name) select new CharacterDropEntryDefinition { Item = entry.Name, Amount = RangeFormatting.FromReference(entry.Drop.AmountMin, entry.Drop.AmountMax, 1, 1), Chance = (IsReferenceDefault(entry.Drop.Chance, 1f) ? ((float?)null) : new float?(entry.Drop.Chance)), OnePerPlayer = (entry.Drop.OnePerPlayer ? new bool?(true) : ((bool?)null)), LevelMultiplier = GetReferenceLevelMultiplierOverride(entry.Drop), DontScale = (entry.Drop.DontScale ? new bool?(true) : ((bool?)null)) }).ToList(); return new CharacterDropPrefabEntry { Prefab = ((Object)snapshot.Prefab).name, Enabled = true, CharacterDrop = new CharacterDropDefinition { Drops = ((list.Count > 0) ? list : null) } }; } private static bool IsReferenceDefault(float value, float defaultValue) { return Math.Abs(value - defaultValue) < 0.0001f; } private static IntRangeDefinition? GetAmountRange(CharacterDropEntryDefinition definition) { return definition.Amount ?? RangeFormatting.From(definition.AmountMin, definition.AmountMax ?? definition.AmountMin); } private static bool? GetReferenceLevelMultiplierOverride(CharacterDropItemSnapshot drop) { bool defaultCharacterDropLevelMultiplier = GetDefaultCharacterDropLevelMultiplier(drop.ItemPrefab); if (drop.LevelMultiplier != defaultCharacterDropLevelMultiplier) { return drop.LevelMultiplier; } return null; } private static string? NormalizeReferenceItemName(GameObject? itemPrefab) { if ((Object)(object)itemPrefab == (Object)null) { return null; } string name = ((Object)itemPrefab).name; if (!name.StartsWith("JVLmock_", StringComparison.OrdinalIgnoreCase)) { return name; } string text = name.Substring("JVLmock_".Length); if (string.IsNullOrWhiteSpace(text)) { return null; } ObjectDB instance = ObjectDB.instance; if (!((Object)(object)((instance != null) ? instance.GetItemPrefab(text) : null) != (Object)null)) { ZNetScene instance2 = ZNetScene.instance; if (!((Object)(object)((instance2 != null) ? instance2.GetPrefab(text) : null) != (Object)null)) { return null; } } return text; } private static string BuildFullScaffoldConfigurationTemplate() { StringBuilder stringBuilder = new StringBuilder(); bool flag = false; foreach (PrefabOwnerSection item in BuildConfigurationTemplate()) { foreach (CharacterDropPrefabEntry entry in item.Entries) { if (flag) { AppendScaffoldBlankLine(stringBuilder); } AppendScaffoldCharacterEntry(stringBuilder, entry); flag = true; } } if (!flag) { return "[]" + Environment.NewLine; } return stringBuilder.ToString(); } private static void AppendScaffoldCharacterEntry(StringBuilder builder, CharacterDropPrefabEntry entry) { AppendScaffoldListEntryLine(builder, 0, "prefab", entry.Prefab); AppendScaffoldLine(builder, 1, "enabled: " + FormatYamlBool(entry.Enabled)); AppendScaffoldConditionsBlock(builder, 1); AppendScaffoldLine(builder, 1, "characterDrop:"); List list = entry.CharacterDrop?.Drops ?? new List(); if (list.Count > 0) { AppendScaffoldLine(builder, 2, "drops:"); { foreach (CharacterDropEntryDefinition item in list) { AppendScaffoldCharacterDropEntry(builder, 3, item); } return; } } AppendScaffoldLine(builder, 2, "drops: []"); } private static void AppendScaffoldCharacterDropEntry(StringBuilder builder, int indent, CharacterDropEntryDefinition definition) { AppendScaffoldListEntryLine(builder, indent, "item", definition.Item); AppendScaffoldLine(builder, indent + 1, "amount: " + RangeFormatting.FormatInlineObject(GetAmountRange(definition) ?? RangeFormatting.From(1, 1))); AppendScaffoldLine(builder, indent + 1, "chance: " + FormatYamlFloat(definition.Chance ?? 1f)); AppendScaffoldLine(builder, indent + 1, "dontScale: " + FormatYamlBool(definition.DontScale == true)); AppendScaffoldLine(builder, indent + 1, "levelMultiplier: " + FormatYamlBool(GetConfiguredCharacterDropLevelMultiplierForOutput(definition))); AppendScaffoldLine(builder, indent + 1, "onePerPlayer: " + FormatYamlBool(definition.OnePerPlayer == true)); AppendScaffoldNullableIntLine(builder, indent + 1, "amountLimit", definition.AmountLimit); AppendScaffoldLine(builder, indent + 1, "dropInStack: " + FormatYamlBool(definition.DropInStack == true)); } private static void AppendScaffoldLine(StringBuilder builder, int indent, string text) { builder.Append(' ', indent * 2); builder.AppendLine(text); } private static void AppendScaffoldBlankLine(StringBuilder builder) { builder.AppendLine(); } private static void AppendScaffoldConditionsBlock(StringBuilder builder, int indent) { AppendScaffoldLine(builder, indent, "conditions:"); AppendScaffoldLine(builder, indent + 1, "level: null"); AppendScaffoldLine(builder, indent + 1, "altitude: null"); AppendScaffoldLine(builder, indent + 1, "distanceFromCenter: null"); AppendScaffoldLine(builder, indent + 1, "biomes: []"); AppendScaffoldLine(builder, indent + 1, "locations: []"); AppendScaffoldLine(builder, indent + 1, "timeOfDay: null"); AppendScaffoldLine(builder, indent + 1, "requiredEnvironments: []"); AppendScaffoldLine(builder, indent + 1, "requiredGlobalKeys: []"); AppendScaffoldLine(builder, indent + 1, "forbiddenGlobalKeys: []"); AppendScaffoldLine(builder, indent + 1, "states: []"); AppendScaffoldLine(builder, indent + 1, "factions: []"); AppendScaffoldLine(builder, indent + 1, "inForest: null"); AppendScaffoldLine(builder, indent + 1, "inDungeon: null"); AppendScaffoldLine(builder, indent + 1, "insidePlayerBase: null"); } private static void AppendScaffoldNullableIntLine(StringBuilder builder, int indent, string key, int? value) { if (value.HasValue) { AppendScaffoldLine(builder, indent, $"{key}: {value.Value}"); } else { AppendScaffoldLine(builder, indent, key + ": null"); } } private static void AppendScaffoldListEntryLine(StringBuilder builder, int indent, string key, string? value) { if (value == null) { AppendScaffoldLine(builder, indent, "- " + key + ": null"); } else { AppendScaffoldLine(builder, indent, "- " + key + ": " + FormatYamlString(value)); } } private static string BuildPrimaryOverrideConfigurationTemplate() { StringBuilder stringBuilder = new StringBuilder(); CommentedYamlTemplateSupport.AppendTemplateComment(stringBuilder, "Any file named " + PluginSettingsFacade.GetYamlDomainSupplementalPrefix("character") + "*.yml or " + PluginSettingsFacade.GetYamlDomainSupplementalPrefix("character") + "*.yaml is also loaded # ex) " + PluginSettingsFacade.GetYamlDomainFilePrefix("character") + "_rand1.yml, " + PluginSettingsFacade.GetYamlDomainFilePrefix("character") + "_rand2.yaml"); CommentedYamlTemplateSupport.AppendTemplateComment(stringBuilder, "Use " + PluginSettingsFacade.GetYamlDomainFilePrefix("character") + ".reference.yml to look up real prefab names and reference values, and run dns:full character for exhaustive field examples"); CommentedYamlTemplateSupport.AppendTemplateBlankLine(stringBuilder); CommentedYamlTemplateSupport.AppendTemplateComment(stringBuilder, "characterDrop"); CommentedYamlTemplateSupport.AppendTemplateBlankLine(stringBuilder); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 0, "- prefab: Greydwarf"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 1, "enabled: true"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 1, "conditions: # If these conditions fail, this custom entry is ignored and the original drops are used"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 2, "level: null # ex) 1~3"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 2, "altitude: null # ex) -1000~1000 # Range in world-height meters"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 2, "distanceFromCenter: null # ex) 0~10000 # Range in meters from the world center"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 2, "biomes: [] # ex) [BlackForest, Mistlands]"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 2, "locations: [] # ex) [Hildir_camp]"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 2, "timeOfDay: null # ex) [day, night]"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 2, "requiredEnvironments: [] # ex) [Clear, Rain]"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 2, "requiredGlobalKeys: [] # ex) [defeated_eikthyr, defeated_gdking]"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 2, "forbiddenGlobalKeys: [] # ex) [nomap, defeated_bonemass]"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 2, "states: [] # ex) [Default, Tamed, Event]"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 2, "factions: [] # ex) [ForestMonsters, Demon]"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 2, "inForest: null # true = forest only # false = outside forest only # null or no field allows both"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 2, "inDungeon: null # true = dungeon only # false = overworld only # null or no field allows both"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 2, "insidePlayerBase: null # true = near player base only # false = away from player base only # null or no field allows both"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 1, "characterDrop:"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 2, "drops: # Set drops: [] to disable character drops for an entry"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 2, "- item: null # ex) Resin # Drop prefab; CharacterDrop can also spawn non-item prefabs such as monsters"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 3, "amount: 1~1 # ex) 1~3 # Range of item amount"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 3, "chance: 1 # Chance from 0 to 1 for this item on each roll"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 3, "dontScale: false # True skips the game's built-in drop scaling for the base amount roll"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 3, "levelMultiplier: true # Omitted default: true for ItemDrop prefabs, false for non-item prefabs # Trophy items can be forced true in config"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 3, "onePerPlayer: false # True uses nearby player count as the final amount # Configure check range in config"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 3, "amountLimit: null # ex) 2 # Integer cap on the final amount"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 3, "dropInStack: false # True spawns one stacked drop instead of many singles"); CommentedYamlTemplateSupport.AppendTemplateBlankLine(stringBuilder); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 0, "- prefab: Eikthyr"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 1, "characterDrop:"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 2, "drops:"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 2, "- item: TrophyEikthyr"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 3, "dontScale: true"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 3, "levelMultiplier: false"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 2, "- item: HardAntler"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 3, "amount: 3"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 3, "dontScale: true"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 3, "levelMultiplier: false"); CommentedYamlTemplateSupport.AppendTemplateBlankLine(stringBuilder); return stringBuilder.ToString(); } private static void RefreshVneiCompatibility(Dictionary previousEntrySignatures) { RefreshVneiCompatibility(previousEntrySignatures, CloneCurrentEntrySignaturesByPrefab()); } private static void RefreshVneiCompatibility(Dictionary previousEntrySignatures, Dictionary currentEntrySignatures) { VneiCompatibility.RefreshCharacterPrefabs(BuildDirtyPrefabs(previousEntrySignatures, currentEntrySignatures)); } internal static bool HasVneiRelevantEntries(string prefabName) { lock (Sync) { List value; return RuntimeState.ActiveEntriesByPrefab.TryGetValue(prefabName ?? "", out value) && value.Any(HasVneiRelevantDrops); } } internal static bool TryGetVneiDisplayResults(CharacterDrop characterDrop, out List results) { lock (Sync) { results = new List(); if ((Object)(object)characterDrop == (Object)null) { return false; } GameObject gameObject = ((Component)characterDrop).gameObject; string text = ((gameObject != null) ? ((Object)gameObject).name : null) ?? ""; List value; List list = (RuntimeState.ActiveEntriesByPrefab.TryGetValue(text, out value) ? value.Where(HasVneiRelevantDrops).ToList() : null); bool flag = list != null && list.Count > 0; CharacterDropSnapshot snapshot; List list2 = (CharacterDropRuntime.TryGetSnapshot(text, out snapshot) ? snapshot.Drops : CloneSnapshotDrops(characterDrop.m_drops)); if (!flag && list2.Count == 0) { return false; } bool num = list?.Any((CharacterDropPrefabEntry entry) => !DropConditionEvaluator.HasCharacterConditions(entry.Conditions)) ?? false; HashSet seen = new HashSet(StringComparer.Ordinal); if (!num) { foreach (CharacterDropItemSnapshot item in list2) { AddVneiSnapshotDrop(results, seen, item); } } IEnumerable enumerable = list; foreach (CharacterDropPrefabEntry item2 in enumerable ?? Enumerable.Empty()) { IEnumerable enumerable2 = item2.CharacterDrop?.Drops; foreach (CharacterDropEntryDefinition item3 in enumerable2 ?? Enumerable.Empty()) { AddVneiConfiguredDrop(results, seen, item3, text + "/characterDrop"); } } return flag || list2.Count > 0; } } private static bool HasVneiRelevantDrops(CharacterDropPrefabEntry entry) { if (entry == null) { return false; } return entry.CharacterDrop?.Drops?.Count > 0; } private static void AddVneiSnapshotDrop(List results, HashSet seen, CharacterDropItemSnapshot drop) { if (!((Object)(object)drop.ItemPrefab == (Object)null)) { string item = string.Format("{0}\n{1}\n{2}\n{3}", ((Object)drop.ItemPrefab).name, drop.AmountMin, drop.AmountMax, drop.Chance.ToString("R")); if (seen.Add(item)) { results.Add(new VneiRecipeResult(((Object)drop.ItemPrefab).name, 1, 1, 1f, Math.Max(1, drop.AmountMin), Math.Max(Math.Max(1, drop.AmountMin), drop.AmountMax), Mathf.Clamp01(drop.Chance))); } } } private static void AddVneiConfiguredDrop(List results, HashSet seen, CharacterDropEntryDefinition definition, string context) { string text = (definition.Item ?? "").Trim(); if (text.Length == 0) { return; } GameObject val = ResolveKnownPrefab(text); if (!((Object)(object)val == (Object)null)) { int num = Math.Max(1, definition.AmountMin ?? 1); int max = Math.Max(num, definition.AmountMax ?? definition.AmountMin ?? 1); float chance = Mathf.Clamp01(definition.Chance ?? 1f); string item = BuildDropRowFingerprint(definition, GetEffectiveCharacterDropLevelMultiplier(definition, val)); if (seen.Add(item)) { results.Add(new VneiRecipeResult(((Object)val).name, 1, 1, 1f, num, max, chance)); } } } } internal static class CommentedYamlTemplateSupport { internal static void AppendTemplateComment(StringBuilder builder, string text) { builder.Append("# "); builder.AppendLine(text); } internal static void AppendTemplateLine(StringBuilder builder, int indent, string text) { builder.Append("# "); builder.Append(' ', indent * 2); builder.AppendLine(text); } internal static void AppendTemplateBlankLine(StringBuilder builder) { builder.AppendLine("#"); } } internal static class ConfigurationEntryCloneSupport { internal static ConditionsDefinition? CloneConditions(ConditionsDefinition? source) { if (source == null) { return null; } return new ConditionsDefinition { Level = CloneIntRange(source.Level), Altitude = CloneFloatRange(source.Altitude), MinLevel = source.MinLevel, MaxLevel = source.MaxLevel, MinAltitude = source.MinAltitude, MaxAltitude = source.MaxAltitude, DistanceFromCenter = CloneFloatRange(source.DistanceFromCenter), MinDistanceFromCenter = source.MinDistanceFromCenter, MaxDistanceFromCenter = source.MaxDistanceFromCenter, Biomes = CloneStringList(source.Biomes), ResolvedBiomeMask = source.ResolvedBiomeMask, Locations = CloneStringList(source.Locations), TimeOfDay = CloneTimeOfDay(source.TimeOfDay), RequiredEnvironments = CloneStringList(source.RequiredEnvironments), RequiredGlobalKeys = CloneStringList(source.RequiredGlobalKeys), ForbiddenGlobalKeys = CloneStringList(source.ForbiddenGlobalKeys), States = CloneStringList(source.States), Factions = CloneStringList(source.Factions), InForest = source.InForest, InDungeon = source.InDungeon, InsidePlayerBase = source.InsidePlayerBase }; } internal static CharacterDropDefinition? CloneCharacterDropDefinition(CharacterDropDefinition? source) { if (source == null) { return null; } return new CharacterDropDefinition { Drops = CloneList(source.Drops, CloneCharacterDropEntryDefinition) }; } private static CharacterDropEntryDefinition CloneCharacterDropEntryDefinition(CharacterDropEntryDefinition source) { return new CharacterDropEntryDefinition { Item = source.Item, Amount = CloneIntRange(source.Amount), AmountMin = source.AmountMin, AmountMax = source.AmountMax, Chance = source.Chance, DontScale = source.DontScale, LevelMultiplier = source.LevelMultiplier, OnePerPlayer = source.OnePerPlayer, AmountLimit = source.AmountLimit, DropInStack = source.DropInStack }; } internal static SpawnAreaDefinition? CloneSpawnAreaDefinition(SpawnAreaDefinition? source) { if (source == null) { return null; } return new SpawnAreaDefinition { LevelUpChance = source.LevelUpChance, SpawnInterval = source.SpawnInterval, TriggerDistance = source.TriggerDistance, SetPatrolSpawnPoint = source.SetPatrolSpawnPoint, SpawnRadius = source.SpawnRadius, NearRadius = source.NearRadius, FarRadius = source.FarRadius, MaxNear = source.MaxNear, MaxTotal = source.MaxTotal, MaxTotalSpawns = source.MaxTotalSpawns, OnGroundOnly = source.OnGroundOnly, Creatures = CloneList(source.Creatures, CloneSpawnAreaSpawnDefinition) }; } private static SpawnAreaSpawnDefinition CloneSpawnAreaSpawnDefinition(SpawnAreaSpawnDefinition source) { return new SpawnAreaSpawnDefinition { Creature = source.Creature, Weight = source.Weight, Level = CloneIntRange(source.Level), MinLevel = source.MinLevel, MaxLevel = source.MaxLevel, Faction = source.Faction, Data = source.Data, Fields = CloneStringDictionary(source.Fields), Objects = CloneStringList(source.Objects) }; } internal static CreatureSpawnerDefinition? CloneCreatureSpawnerDefinition(CreatureSpawnerDefinition? source) { if (source == null) { return null; } return new CreatureSpawnerDefinition { Creature = source.Creature, TimeOfDay = CloneTimeOfDay(source.TimeOfDay), RequiredGlobalKey = source.RequiredGlobalKey, BlockingGlobalKey = source.BlockingGlobalKey, Level = CloneIntRange(source.Level), MinLevel = source.MinLevel, MaxLevel = source.MaxLevel, LevelUpChance = source.LevelUpChance, RespawnTimeMinutes = source.RespawnTimeMinutes, SpawnCheckInterval = source.SpawnCheckInterval, SpawnGroupId = source.SpawnGroupId, SpawnGroupRadius = source.SpawnGroupRadius, SpawnerWeight = source.SpawnerWeight, MaxGroupSpawned = source.MaxGroupSpawned, TriggerDistance = source.TriggerDistance, TriggerNoise = source.TriggerNoise, RequireSpawnArea = source.RequireSpawnArea, AllowInsidePlayerBase = source.AllowInsidePlayerBase, WakeUpAnimation = source.WakeUpAnimation, SetPatrolSpawnPoint = source.SetPatrolSpawnPoint, Faction = source.Faction, Data = source.Data, Fields = CloneStringDictionary(source.Fields), Objects = CloneStringList(source.Objects) }; } private static IntRangeDefinition? CloneIntRange(IntRangeDefinition? source) { if (source == null) { return null; } return new IntRangeDefinition { Min = source.Min, Max = source.Max }; } private static FloatRangeDefinition? CloneFloatRange(FloatRangeDefinition? source) { if (source == null) { return null; } return new FloatRangeDefinition { Min = source.Min, Max = source.Max }; } private static TimeOfDayDefinition? CloneTimeOfDay(TimeOfDayDefinition? source) { if (source == null) { return null; } return new TimeOfDayDefinition { Values = (source.Values?.ToList() ?? new List()) }; } private static List? CloneList(List? source, Func cloneItem) { if (source == null) { return null; } List list = new List(source.Count); foreach (T item in source) { list.Add(cloneItem(item)); } return list; } private static List? CloneStringList(List? source) { return source?.ToList(); } private static Dictionary? CloneStringDictionary(Dictionary? source) { if (source != null) { return new Dictionary(source); } return null; } internal static EventConditionsDefinition? CloneEventConditionsDefinition(EventConditionsDefinition? source) { if (source == null) { return null; } return new EventConditionsDefinition { Biomes = CloneStringList(source.Biomes), PlayerBase = CloneStringList(source.PlayerBase), RequiredEnvironments = CloneStringList(source.RequiredEnvironments), Players = CloneStringList(source.Players), RequiredGlobalKeys = CloneStringList(source.RequiredGlobalKeys), ForbiddenGlobalKeys = CloneStringList(source.ForbiddenGlobalKeys), RequiredKnownItems = CloneStringList(source.RequiredKnownItems), ForbiddenKnownItems = CloneStringList(source.ForbiddenKnownItems), RequiredPlayerKeysAny = CloneStringList(source.RequiredPlayerKeysAny), RequiredPlayerKeysAll = CloneStringList(source.RequiredPlayerKeysAll), ForbiddenPlayerKeys = CloneStringList(source.ForbiddenPlayerKeys) }; } internal static EventSpawnDefinition? CloneEventSpawnDefinition(EventSpawnDefinition? source) { if (source == null) { return null; } return new EventSpawnDefinition { Prefab = source.Prefab, Enabled = source.Enabled, SpawnSystem = CloneSpawnSystemSpawnDefinition(source.SpawnSystem) }; } internal static PrefabConfigurationEntry ClonePrefabConfigurationEntry(PrefabConfigurationEntry source) { return new PrefabConfigurationEntry { RuleId = source.RuleId, Prefab = source.Prefab, Enabled = source.Enabled, Conditions = CloneConditions(source.Conditions), DropOnDestroyed = CloneDropTableDefinition(source.DropOnDestroyed), MineRock = CloneDamageableDropTableDefinition(source.MineRock), MineRock5 = CloneDamageableDropTableDefinition(source.MineRock5), TreeBase = CloneDamageableDropTableDefinition(source.TreeBase), TreeLog = CloneDamageableDropTableDefinition(source.TreeLog), Container = CloneDropTableDefinition(source.Container), PickableItem = ClonePickableItemDefinition(source.PickableItem), Pickable = ClonePickableDefinition(source.Pickable), Fish = CloneFishDefinition(source.Fish), Destructible = CloneDestructibleDefinition(source.Destructible), SourcePath = source.SourcePath, SourceLine = source.SourceLine, SourceColumn = source.SourceColumn }; } internal static DropTableDefinition? CloneDropTableDefinition(DropTableDefinition? source) { if (source == null) { return null; } DropTableDefinition dropTableDefinition = new DropTableDefinition(); CopyDropTablePayload(source, dropTableDefinition); return dropTableDefinition; } internal static DamageableDropTableDefinition? CloneDamageableDropTableDefinition(DamageableDropTableDefinition? source) { if (source == null) { return null; } DamageableDropTableDefinition damageableDropTableDefinition = new DamageableDropTableDefinition { Health = source.Health, MinToolTier = source.MinToolTier }; CopyDropTablePayload(source, damageableDropTableDefinition); return damageableDropTableDefinition; } private static void CopyDropTablePayload(DropTablePayloadDefinition source, DropTablePayloadDefinition target) { target.Rolls = CloneIntRange(source.Rolls); target.DropMin = source.DropMin; target.DropMax = source.DropMax; target.DropChance = source.DropChance; target.OneOfEach = source.OneOfEach; target.Drops = CloneList(source.Drops, CloneDropEntryDefinition); } private static DropEntryDefinition CloneDropEntryDefinition(DropEntryDefinition source) { return new DropEntryDefinition { Item = source.Item, Stack = CloneIntRange(source.Stack), StackMin = source.StackMin, StackMax = source.StackMax, Weight = source.Weight, DontScale = source.DontScale }; } internal static DestructibleDefinition? CloneDestructibleDefinition(DestructibleDefinition? source) { if (source == null) { return null; } return new DestructibleDefinition { Health = source.Health, MinToolTier = source.MinToolTier, DestructibleType = source.DestructibleType, SpawnWhenDestroyed = source.SpawnWhenDestroyed }; } internal static PickableDefinition? ClonePickableDefinition(PickableDefinition? source) { if (source == null) { return null; } return new PickableDefinition { OverrideName = source.OverrideName, Drop = ClonePickableDropDefinition(source.Drop), ExtraDrops = CloneDropTablePayloadDefinition(source.ExtraDrops) }; } private static PickableDropDefinition? ClonePickableDropDefinition(PickableDropDefinition? source) { if (source == null) { return null; } return new PickableDropDefinition { Item = source.Item, Amount = source.Amount, MinAmountScaled = source.MinAmountScaled, DontScale = source.DontScale }; } private static DropTablePayloadDefinition? CloneDropTablePayloadDefinition(DropTablePayloadDefinition? source) { if (source == null) { return null; } DropTablePayloadDefinition dropTablePayloadDefinition = new DropTablePayloadDefinition(); CopyDropTablePayload(source, dropTablePayloadDefinition); return dropTablePayloadDefinition; } internal static PickableItemDefinition? ClonePickableItemDefinition(PickableItemDefinition? source) { if (source == null) { return null; } return new PickableItemDefinition { RandomDrops = CloneList(source.RandomDrops, CloneRandomPickableItemDefinition), Drop = ClonePickableItemDropDefinition(source.Drop) }; } private static PickableItemDropDefinition? ClonePickableItemDropDefinition(PickableItemDropDefinition? source) { if (source == null) { return null; } return new PickableItemDropDefinition { Item = source.Item, Stack = source.Stack }; } private static RandomPickableItemDefinition CloneRandomPickableItemDefinition(RandomPickableItemDefinition source) { return new RandomPickableItemDefinition { Item = source.Item, Stack = CloneIntRange(source.Stack), StackMin = source.StackMin, StackMax = source.StackMax, Weight = source.Weight }; } internal static FishDefinition? CloneFishDefinition(FishDefinition? source) { if (source == null) { return null; } return new FishDefinition { ExtraDrops = CloneDropTablePayloadDefinition(source.ExtraDrops) }; } internal static SpawnSystemSpawnDefinition? CloneSpawnSystemSpawnDefinition(SpawnSystemSpawnDefinition? source) { if (source == null) { return null; } return new SpawnSystemSpawnDefinition { Name = source.Name, HuntPlayer = source.HuntPlayer, Level = CloneIntRange(source.Level), MinLevel = source.MinLevel, MaxLevel = source.MaxLevel, OverrideLevelUpChance = source.OverrideLevelUpChance, LevelUpMinCenterDistance = source.LevelUpMinCenterDistance, GroundOffset = source.GroundOffset, GroundOffsetRandom = source.GroundOffsetRandom, SpawnInterval = source.SpawnInterval, SpawnChance = source.SpawnChance, SpawnRadius = CloneFloatRange(source.SpawnRadius), SpawnRadiusMin = source.SpawnRadiusMin, SpawnRadiusMax = source.SpawnRadiusMax, GroupSize = CloneIntRange(source.GroupSize), GroupSizeMin = source.GroupSizeMin, GroupSizeMax = source.GroupSizeMax, GroupRadius = source.GroupRadius, NoSpawnRadius = source.NoSpawnRadius, MaxSpawned = source.MaxSpawned, Tilt = CloneFloatRange(source.Tilt), MinTilt = source.MinTilt, MaxTilt = source.MaxTilt, Altitude = CloneFloatRange(source.Altitude), MinAltitude = source.MinAltitude, MaxAltitude = source.MaxAltitude, OceanDepth = CloneFloatRange(source.OceanDepth), MinOceanDepth = source.MinOceanDepth, MaxOceanDepth = source.MaxOceanDepth, DistanceFromCenter = CloneFloatRange(source.DistanceFromCenter), MinDistanceFromCenter = source.MinDistanceFromCenter, MaxDistanceFromCenter = source.MaxDistanceFromCenter, Biomes = CloneStringList(source.Biomes), ResolvedBiomeMask = source.ResolvedBiomeMask, BiomeAreas = CloneStringList(source.BiomeAreas), TimeOfDay = CloneTimeOfDay(source.TimeOfDay), RequiredEnvironments = CloneStringList(source.RequiredEnvironments), RequiredGlobalKey = source.RequiredGlobalKey, InLava = source.InLava, InForest = source.InForest, InsidePlayerBase = source.InsidePlayerBase, CanSpawnCloseToPlayer = source.CanSpawnCloseToPlayer, Fields = CloneStringDictionary(source.Fields), Objects = CloneStringList(source.Objects), Data = source.Data, Faction = source.Faction }; } } internal enum DomainReloadOutcome { NoChange, Loaded, Rejected, DeferredStrictValidation, WaitingForPayload, Failed } internal sealed class DomainLoadState { internal string LastLoadedPayload { get; set; } = ""; internal string LastRejectedPayload { get; set; } = ""; internal string PendingStrictPayload { get; set; } = ""; internal string LastRejectedValidationKey { get; set; } = ""; } internal sealed class LocalLoadResult { internal List Entries { get; set; } = new List(); internal List Errors { get; set; } = new List(); internal List Warnings { get; set; } = new List(); internal int ParsedEntryCount { get; set; } internal int LoadedFileCount { get; set; } } internal sealed class StrictValidationResult { internal List Entries { get; set; } = new List(); internal List Warnings { get; set; } = new List(); } internal delegate bool TryGetSyncedEntriesDelegate(out List entries, out string payloadToken); internal sealed class DomainLoadHooks { internal Func, LocalLoadResult> ParseLocalDocuments { get; } internal Func, string, TState> BuildSyncedState { get; } internal Action CommitState { get; } internal Action> RejectLocalPayload { get; } internal Func GetAcceptedEntryCount { get; } internal Action>? LogPartiallyAcceptedLocalConfiguration { get; } internal Action? LogLocalLoadSuccess { get; } internal Action? OnUnchangedPayload { get; } internal Action? PublishCommittedState { get; } internal Func, bool>? CanStrictValidateNow { get; } internal Func, StrictValidationResult>? StrictValidateLocal { get; } internal DomainLoadHooks(Func, LocalLoadResult> parseLocalDocuments, Func, string, TState> buildSyncedState, Action commitState, Action> rejectLocalPayload, Func getAcceptedEntryCount, Action>? logPartiallyAcceptedLocalConfiguration = null, Action? logLocalLoadSuccess = null, Action? onUnchangedPayload = null, Action? publishCommittedState = null, Func, bool>? canStrictValidateNow = null, Func, StrictValidationResult>? strictValidateLocal = null) { ParseLocalDocuments = parseLocalDocuments; BuildSyncedState = buildSyncedState; CommitState = commitState; RejectLocalPayload = rejectLocalPayload; GetAcceptedEntryCount = getAcceptedEntryCount; LogPartiallyAcceptedLocalConfiguration = logPartiallyAcceptedLocalConfiguration; LogLocalLoadSuccess = logLocalLoadSuccess; OnUnchangedPayload = onUnchangedPayload; PublishCommittedState = publishCommittedState; CanStrictValidateNow = canStrictValidateNow; StrictValidateLocal = strictValidateLocal; } } internal sealed class DomainSyncHooks { internal TryGetSyncedEntriesDelegate TryGetSyncedEntries { get; } internal Func ShouldSkipPayload { get; } internal Func, string, TState> BuildSyncedState { get; } internal Action CommitState { get; } internal Func GetAcceptedEntryCount { get; } internal string SourceName { get; } internal Action? OnWaitingForPayload { get; } internal Action? LogSyncedLoadSuccess { get; } internal Action? LogSyncedLoadFailure { get; } internal DomainSyncHooks(TryGetSyncedEntriesDelegate tryGetSyncedEntries, Func shouldSkipPayload, Func, string, TState> buildSyncedState, Action commitState, Func getAcceptedEntryCount, string sourceName, Action? onWaitingForPayload = null, Action? logSyncedLoadSuccess = null, Action? logSyncedLoadFailure = null) { TryGetSyncedEntries = tryGetSyncedEntries; ShouldSkipPayload = shouldSkipPayload; BuildSyncedState = buildSyncedState; CommitState = commitState; GetAcceptedEntryCount = getAcceptedEntryCount; SourceName = sourceName; OnWaitingForPayload = onWaitingForPayload; LogSyncedLoadSuccess = logSyncedLoadSuccess; LogSyncedLoadFailure = logSyncedLoadFailure; } } internal sealed class DomainConfigurationRuntime { private readonly DomainLoadHooks _loadHooks; private readonly DomainSyncHooks _syncHooks; internal DomainLoadState LoadState { get; } = new DomainLoadState(); internal DomainConfigurationRuntime(DomainLoadHooks loadHooks, DomainSyncHooks syncHooks) { _loadHooks = loadHooks ?? throw new ArgumentNullException("loadHooks"); _syncHooks = syncHooks ?? throw new ArgumentNullException("syncHooks"); } internal DomainReloadOutcome ReloadSourceOfTruth(List overridePaths) { return ConfigurationDomainHost.RunSourceOfTruthReload(LoadState, overridePaths, _loadHooks); } internal DomainReloadOutcome ReloadSynced() { return ConfigurationDomainHost.RunSyncedReload(_syncHooks); } internal void ResetLoadState() { ConfigurationDomainHost.ResetLoadState(LoadState); } internal void MarkSyncedPayloadPending(bool isSourceOfTruth, Action? onClientPending = null) { if (!isSourceOfTruth) { onClientPending?.Invoke(); } } internal void EnterPendingSyncedPayloadState(bool isSourceOfTruth, Action? beforeResetLoadState = null, Action? afterResetLoadState = null) { if (!isSourceOfTruth) { beforeResetLoadState?.Invoke(); ResetLoadState(); afterResetLoadState?.Invoke(); } } internal bool ApplySyncedPayload(Action? onLoaded = null) { if (ReloadSynced() != DomainReloadOutcome.Loaded) { return false; } onLoaded?.Invoke(); return true; } } internal static class ConfigurationDomainHost { internal static void PublishSyncedPayload(bool isSourceOfTruth, DomainDescriptor descriptor, List entries, string payloadSignature) { if (isSourceOfTruth) { PluginManifestCoordinator.PublishSyncedPayload(descriptor, entries, payloadSignature); } } internal static bool TryGetSyncedEntries(DomainDescriptor descriptor, out List entries, out string payloadToken) { return PluginManifestCoordinator.TryGetSyncedEntries(descriptor, out entries, out payloadToken); } internal static bool ShouldSkipSyncedPayload(DomainLoadState loadState, string payloadToken, bool isPayloadReady) { if (isPayloadReady) { return string.Equals(loadState.LastLoadedPayload, payloadToken, StringComparison.Ordinal); } return false; } internal static void ResetLoadState(DomainLoadState loadState) { if (loadState != null) { loadState.LastLoadedPayload = ""; loadState.LastRejectedPayload = ""; loadState.PendingStrictPayload = ""; loadState.LastRejectedValidationKey = ""; } } internal static void RejectLocalConfigurationPayload(DomainLoadState loadState, string payload, IEnumerable errors, string domainName, string? validationKey = null) { bool num = string.Equals(loadState.LastRejectedPayload, payload, StringComparison.Ordinal); bool flag = validationKey == null || string.Equals(loadState.LastRejectedValidationKey, validationKey, StringComparison.Ordinal); if (num && flag) { return; } loadState.LastRejectedPayload = payload; loadState.PendingStrictPayload = ""; loadState.LastRejectedValidationKey = validationKey ?? ""; DropNSpawnPlugin.DropNSpawnLogger.LogError((object)("Rejected " + domainName + " reload. Keeping the previous authoritative " + domainName + " configuration.")); foreach (string item in errors.Where((string message) => !string.IsNullOrWhiteSpace(message)).Distinct(StringComparer.OrdinalIgnoreCase)) { DropNSpawnPlugin.DropNSpawnLogger.LogError((object)item); } } internal static DomainReloadOutcome RunSourceOfTruthReload(DomainLoadState loadState, List overridePaths, DomainLoadHooks hooks) { List list = ConfigurationLoadSupport.ReadLocalYamlDocuments(overridePaths); string text = ConfigurationLoadSupport.BuildLocalPayload(list); bool flag = loadState.PendingStrictPayload.Length > 0 && string.Equals(loadState.PendingStrictPayload, text, StringComparison.Ordinal); if (string.Equals(loadState.LastLoadedPayload, text, StringComparison.Ordinal) && !flag) { loadState.LastRejectedPayload = ""; loadState.PendingStrictPayload = ""; hooks.OnUnchangedPayload?.Invoke(); return DomainReloadOutcome.NoChange; } LocalLoadResult localLoadResult = hooks.ParseLocalDocuments(list); if (localLoadResult.Errors.Count > 0) { loadState.PendingStrictPayload = ""; hooks.RejectLocalPayload(text, localLoadResult.Errors); return DomainReloadOutcome.Rejected; } List entries = localLoadResult.Entries; List list2 = ((localLoadResult.Warnings.Count > 0) ? new List(localLoadResult.Warnings) : new List()); if (hooks.CanStrictValidateNow != null && hooks.StrictValidateLocal != null) { if (!hooks.CanStrictValidateNow(entries)) { loadState.PendingStrictPayload = text; return DomainReloadOutcome.DeferredStrictValidation; } loadState.PendingStrictPayload = ""; StrictValidationResult strictValidationResult = hooks.StrictValidateLocal(entries); entries = strictValidationResult.Entries; if (strictValidationResult.Warnings.Count > 0) { list2.AddRange(strictValidationResult.Warnings); } } TState val = hooks.BuildSyncedState(entries, ""); hooks.CommitState(val, text); int num = hooks.GetAcceptedEntryCount(val); if (list2.Count > 0) { hooks.LogPartiallyAcceptedLocalConfiguration?.Invoke(localLoadResult.ParsedEntryCount, num, list2); } hooks.LogLocalLoadSuccess?.Invoke(num, localLoadResult.LoadedFileCount); hooks.PublishCommittedState?.Invoke(); return DomainReloadOutcome.Loaded; } internal static DomainReloadOutcome RunSyncedReload(DomainSyncHooks hooks) { if (!hooks.TryGetSyncedEntries(out var entries, out var payloadToken)) { hooks.OnWaitingForPayload?.Invoke(); return DomainReloadOutcome.WaitingForPayload; } if (hooks.ShouldSkipPayload(payloadToken)) { return DomainReloadOutcome.NoChange; } try { TState val = hooks.BuildSyncedState(entries, hooks.SourceName); hooks.CommitState(val, payloadToken); hooks.LogSyncedLoadSuccess?.Invoke(payloadToken, hooks.GetAcceptedEntryCount(val)); return DomainReloadOutcome.Loaded; } catch (Exception arg) { hooks.LogSyncedLoadFailure?.Invoke(payloadToken, arg); return DomainReloadOutcome.Failed; } } } internal static class DomainConfigurationFileSupport { internal static bool HasAnyOverrideConfigurationFile(string domain, string primaryYmlPath, string primaryYamlPath) { if (!File.Exists(primaryYmlPath) && !File.Exists(primaryYamlPath)) { return EnumerateSupplementalOverrideConfigurationPaths(domain, (string fileName) => IsOverrideConfigurationFileName(domain, fileName)).Any(); } return true; } internal static IEnumerable EnumerateOverrideConfigurationPaths(string domain, string primaryYmlPath, string primaryYamlPath, bool usePreferredPrimaryPath = false, Action? warn = null) { if (!Directory.Exists(DropNSpawnPlugin.YamlConfigDirectoryPath)) { yield break; } if (usePreferredPrimaryPath) { string preferredPrimaryOverridePath = GetPreferredPrimaryOverridePath(primaryYmlPath, primaryYamlPath, warn ?? ((Action)delegate { })); if (preferredPrimaryOverridePath != null) { yield return preferredPrimaryOverridePath; } } else { if (File.Exists(primaryYmlPath)) { yield return primaryYmlPath; } if (File.Exists(primaryYamlPath)) { yield return primaryYamlPath; } } foreach (string item in EnumerateSupplementalOverrideConfigurationPaths(domain, (string fileName) => IsOverrideConfigurationFileName(domain, fileName))) { yield return item; } } internal static IEnumerable EnumerateSupplementalOverrideConfigurationPaths(string domain, Func isOverrideFileName) { return PluginSettingsFacade.EnumerateSupplementalOverrideConfigurationPaths(PluginSettingsFacade.GetYamlDomainSupplementalPrefix(domain) + "*.*", isOverrideFileName); } internal static string? GetPreferredPrimaryOverridePath(string primaryYmlPath, string primaryYamlPath, Action warn) { bool flag = File.Exists(primaryYmlPath); bool flag2 = File.Exists(primaryYamlPath); if (!flag && !flag2) { return null; } if (flag && flag2) { warn("Both '" + Path.GetFileName(primaryYmlPath) + "' and '" + Path.GetFileName(primaryYamlPath) + "' exist. Using '" + Path.GetFileName(primaryYmlPath) + "'."); } if (!flag) { return primaryYamlPath; } return primaryYmlPath; } internal static bool IsOverrideConfigurationFileName(string domain, string fileName) { if (string.IsNullOrWhiteSpace(domain) || string.IsNullOrWhiteSpace(fileName)) { return false; } if (!fileName.Equals(PluginSettingsFacade.GetYamlDomainFilePrefix(domain) + ".yml", StringComparison.OrdinalIgnoreCase) && !fileName.Equals(PluginSettingsFacade.GetYamlDomainFilePrefix(domain) + ".yaml", StringComparison.OrdinalIgnoreCase)) { if (fileName.StartsWith(PluginSettingsFacade.GetYamlDomainSupplementalPrefix(domain), StringComparison.OrdinalIgnoreCase)) { if (!fileName.EndsWith(".yml", StringComparison.OrdinalIgnoreCase)) { return fileName.EndsWith(".yaml", StringComparison.OrdinalIgnoreCase); } return true; } return false; } return true; } } internal abstract class DomainRegistration { internal DomainDescriptor Descriptor { get; } internal DomainTransportMetadata TransportMetadata { get; } internal Action InitializeRuntime { get; } protected DomainRegistration(DomainDescriptor descriptor, DomainTransportMetadata transportMetadata, Action initializeRuntime) { Descriptor = descriptor ?? throw new ArgumentNullException("descriptor"); TransportMetadata = transportMetadata ?? throw new ArgumentNullException("transportMetadata"); InitializeRuntime = initializeRuntime ?? throw new ArgumentNullException("initializeRuntime"); } } internal sealed class DomainModuleOptions { internal string DomainKey { get; set; } = ""; internal DropNSpawnPlugin.ReloadDomain ReloadDomain { get; set; } internal string ManifestSettingKey { get; set; } = ""; internal int ManifestPriority { get; set; } internal Func? ShouldReloadForPath { get; set; } internal Action? Reload { get; set; } internal Action? InitializeRuntime { get; set; } internal Action? OnGameDataReady { get; set; } internal Func? HandleExpandWorldDataReady { get; set; } internal int DtoVersion { get; set; } internal DomainTransportProfile TransportProfile { get; set; } internal string DisplayName { get; set; } = ""; internal string CacheDirectoryName { get; set; } = ""; internal int ClientRequestPriority { get; set; } internal Func? KeySelector { get; set; } internal Action? ApplyPayloadAction { get; set; } internal Func? HasPendingSnapshotBuildWork { get; set; } internal Func? ProcessPendingSnapshotBuildStep { get; set; } internal Func? HasPendingReconcileWork { get; set; } internal Func? ProcessPendingReconcileStep { get; set; } internal Action? BeforeClientManifestChanged { get; set; } internal Action? OnClientAuthorityCutover { get; set; } } internal sealed class DomainModuleDefinition : DomainRegistration { internal DomainDescriptor DescriptorTyped { get; } internal DomainTransportMetadata TransportMetadataTyped { get; } internal DomainModuleDefinition(DomainModuleOptions options) : this(CreateDescriptor(options.DomainKey, options.ReloadDomain, options.ManifestSettingKey, options.ManifestPriority, Require(options.ShouldReloadForPath, "ShouldReloadForPath"), Require(options.Reload, "Reload"), Require(options.OnGameDataReady, "OnGameDataReady"), Require(options.HandleExpandWorldDataReady, "HandleExpandWorldDataReady"), options.HasPendingSnapshotBuildWork, options.ProcessPendingSnapshotBuildStep, options.HasPendingReconcileWork, options.ProcessPendingReconcileStep, options.BeforeClientManifestChanged, options.OnClientAuthorityCutover), options.DtoVersion, options.TransportProfile, options.DisplayName, options.CacheDirectoryName, options.ClientRequestPriority, Require(options.KeySelector, "KeySelector"), Require(options.ApplyPayloadAction, "ApplyPayloadAction"), Require(options.InitializeRuntime, "InitializeRuntime")) { } private DomainModuleDefinition(DomainDescriptor descriptor, int dtoVersion, DomainTransportProfile transportProfile, string displayName, string cacheDirectoryName, int clientRequestPriority, Func keySelector, Action applyPayloadAction, Action initializeRuntime) : base(descriptor, new DomainTransportMetadata(descriptor.DomainKey, dtoVersion, transportProfile, displayName, cacheDirectoryName, clientRequestPriority, keySelector, applyPayloadAction), initializeRuntime) { DescriptorTyped = descriptor; TransportMetadataTyped = (DomainTransportMetadata)base.TransportMetadata; } private static DomainDescriptor CreateDescriptor(string domainKey, DropNSpawnPlugin.ReloadDomain reloadDomain, string manifestSettingKey, int manifestPriority, Func shouldReloadForPath, Action reload, Action onGameDataReady, Func handleExpandWorldDataReady, Func? hasPendingSnapshotBuildWork, Func? processPendingSnapshotBuildStep, Func? hasPendingReconcileWork, Func? processPendingReconcileStep, Action? beforeClientManifestChanged, Action? onClientAuthorityCutover) { return new DomainDescriptor(domainKey, reloadDomain, manifestSettingKey, manifestPriority, shouldReloadForPath, reload, onGameDataReady, handleExpandWorldDataReady, hasPendingSnapshotBuildWork, processPendingSnapshotBuildStep, hasPendingReconcileWork, processPendingReconcileStep, beforeClientManifestChanged, onClientAuthorityCutover); } private static T Require(T? value, string name) where T : class { return value ?? throw new ArgumentNullException(name); } } internal abstract class DomainDescriptor { internal string DomainKey { get; } internal DropNSpawnPlugin.ReloadDomain ReloadDomain { get; } internal string ManifestSettingKey { get; } internal int ManifestPriority { get; } internal Func ShouldReloadForPath { get; } internal Action Reload { get; } internal Action OnGameDataReady { get; } internal Func HandleExpandWorldDataReady { get; } internal Func? HasPendingSnapshotBuildWork { get; } internal Func? ProcessPendingSnapshotBuildStep { get; } internal Func? HasPendingReconcileWork { get; } internal Func? ProcessPendingReconcileStep { get; } internal Action? BeforeClientManifestChanged { get; } internal Action? OnClientAuthorityCutover { get; } protected DomainDescriptor(string domainKey, DropNSpawnPlugin.ReloadDomain reloadDomain, string manifestSettingKey, int manifestPriority, Func shouldReloadForPath, Action reload, Action onGameDataReady, Func handleExpandWorldDataReady, Func? hasPendingSnapshotBuildWork = null, Func? processPendingSnapshotBuildStep = null, Func? hasPendingReconcileWork = null, Func? processPendingReconcileStep = null, Action? beforeClientManifestChanged = null, Action? onClientAuthorityCutover = null) { DomainKey = domainKey ?? ""; ReloadDomain = reloadDomain; ManifestSettingKey = manifestSettingKey ?? ""; ManifestPriority = manifestPriority; ShouldReloadForPath = shouldReloadForPath; Reload = reload; OnGameDataReady = onGameDataReady; HandleExpandWorldDataReady = handleExpandWorldDataReady; HasPendingSnapshotBuildWork = hasPendingSnapshotBuildWork; ProcessPendingSnapshotBuildStep = processPendingSnapshotBuildStep; HasPendingReconcileWork = hasPendingReconcileWork; ProcessPendingReconcileStep = processPendingReconcileStep; BeforeClientManifestChanged = beforeClientManifestChanged; OnClientAuthorityCutover = onClientAuthorityCutover; } internal abstract void HandleManifestChanged(string manifestRaw); } internal sealed class DomainDescriptor : DomainDescriptor { internal DomainDescriptor(string domainKey, DropNSpawnPlugin.ReloadDomain reloadDomain, string manifestSettingKey, int manifestPriority, Func shouldReloadForPath, Action reload, Action onGameDataReady, Func handleExpandWorldDataReady, Func? hasPendingSnapshotBuildWork = null, Func? processPendingSnapshotBuildStep = null, Func? hasPendingReconcileWork = null, Func? processPendingReconcileStep = null, Action? beforeClientManifestChanged = null, Action? onClientAuthorityCutover = null) : base(domainKey, reloadDomain, manifestSettingKey, manifestPriority, shouldReloadForPath, reload, onGameDataReady, handleExpandWorldDataReady, hasPendingSnapshotBuildWork, processPendingSnapshotBuildStep, hasPendingReconcileWork, processPendingReconcileStep, beforeClientManifestChanged, onClientAuthorityCutover) { } internal override void HandleManifestChanged(string manifestRaw) { NetworkPayloadSyncSupport.HandleManifestChanged(this, manifestRaw); } } internal static class DomainRegistry { internal static readonly DomainRegistration[] AllRegistrations = new DomainRegistration[5] { ObjectDropManager.Module, CharacterDropManager.Module, SpawnerManager.Module, SpawnSystemManager.Module, EventManager.Module }; internal static readonly DomainDescriptor[] RuntimeDomains = AllRegistrations.Select((DomainRegistration registration) => registration.Descriptor).ToArray(); internal static readonly DomainDescriptor[] SnapshotBuildDomains = RuntimeDomains.Where(HasSnapshotBuildHooks).ToArray(); internal static readonly DomainDescriptor[] ReconcileDomains = RuntimeDomains.Where(HasReconcileHooks).ToArray(); internal static readonly DomainTransportMetadata[] Transports = AllRegistrations.Select((DomainRegistration registration) => registration.TransportMetadata).ToArray(); internal static void InitializeRuntimeDomains() { DomainRegistration[] allRegistrations = AllRegistrations; for (int i = 0; i < allRegistrations.Length; i++) { allRegistrations[i].InitializeRuntime(); } } private static bool HasSnapshotBuildHooks(DomainDescriptor domain) { return ValidateWorkHookPair(domain, "snapshot-build", domain.HasPendingSnapshotBuildWork != null, domain.ProcessPendingSnapshotBuildStep != null); } private static bool HasReconcileHooks(DomainDescriptor domain) { return ValidateWorkHookPair(domain, "reconcile", domain.HasPendingReconcileWork != null, domain.ProcessPendingReconcileStep != null); } private static bool ValidateWorkHookPair(DomainDescriptor domain, string workKind, bool hasPendingHook, bool hasProcessHook) { if (hasPendingHook != hasProcessHook) { throw new InvalidOperationException("Domain '" + domain.DomainKey + "' has an incomplete " + workKind + " coordinator hook pair."); } return hasPendingHook; } } internal enum DomainTransportProfile { SmallConfig, MediumConfig, LargeConfig, LargeWithArtifacts } internal abstract class DomainTransportMetadata { internal string DomainKey { get; } internal int DtoVersion { get; } internal DomainTransportProfile TransportProfile { get; } internal string DisplayName { get; } internal string CacheDirectoryName { get; } internal int ClientRequestPriority { get; } protected DomainTransportMetadata(string domainKey, int dtoVersion, DomainTransportProfile transportProfile, string displayName, string cacheDirectoryName, int clientRequestPriority) { DomainKey = domainKey ?? ""; DtoVersion = Math.Max(0, dtoVersion); TransportProfile = transportProfile; DisplayName = displayName ?? ""; CacheDirectoryName = cacheDirectoryName ?? ""; ClientRequestPriority = clientRequestPriority; } } internal sealed class DomainTransportMetadata : DomainTransportMetadata { internal Func KeySelector { get; } internal Action ApplyPayloadAction { get; } internal DomainTransportMetadata(string domainKey, int dtoVersion, DomainTransportProfile transportProfile, string displayName, string cacheDirectoryName, int clientRequestPriority, Func keySelector, Action applyPayloadAction) : base(domainKey, dtoVersion, transportProfile, displayName, cacheDirectoryName, clientRequestPriority) { KeySelector = keySelector ?? throw new ArgumentNullException("keySelector"); ApplyPayloadAction = applyPayloadAction ?? throw new ArgumentNullException("applyPayloadAction"); } } internal static class DomainDictionaryDiffSupport { internal static HashSet BuildDirtyKeys(IReadOnlyDictionary previous, IReadOnlyDictionary current) { HashSet hashSet = new HashSet(StringComparer.OrdinalIgnoreCase); foreach (var (text3, b) in current) { if (!previous.TryGetValue(text3, out string value) || !string.Equals(value, b, StringComparison.Ordinal)) { hashSet.Add(text3); } } foreach (string key in previous.Keys) { if (!current.ContainsKey(key)) { hashSet.Add(key); } } return hashSet; } internal static void ReplaceEntries(Dictionary target, IReadOnlyDictionary source) { target.Clear(); foreach (KeyValuePair item in source) { item.Deconstruct(out var key, out var value); string key2 = key; TValue value2 = value; target[key2] = value2; } } } internal static class DomainEntrySignatureSupport { internal static Dictionary BuildSignaturesByKey(IEnumerable>>? entriesByKey, Func, string> computeSignature) { Dictionary dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); if (entriesByKey == null) { return dictionary; } foreach (KeyValuePair> item in entriesByKey) { item.Deconstruct(out var key, out var value); string key2 = key; List arg = value; dictionary[key2] = computeSignature(arg); } return dictionary; } } internal enum StandardApplyStage { None, Validate, RestoreStaticBaseline, ApplyStaticBaseline, PrepareLiveBaseline, ApplyLive, Commit } [Flags] internal enum BaselineDesiredStateCapabilities { None = 0, Validation = 1, StaticBaseline = 2, StaticApply = 4, LiveBaseline = 8, LiveApply = 0x10, StaticRollback = 0x20 } internal abstract class StandardBaselineDesiredStateOperations { public abstract string DomainKey { get; } public abstract BaselineDesiredStateCapabilities Capabilities { get; } public virtual void Validate(TDesiredState desiredState) { } public virtual void RestoreStaticBaseline(TDesiredState desiredState) { } public virtual void ApplyDesiredStateToStaticBaseline(TDesiredState desiredState) { } public virtual void PrepareLiveBaseline(TDesiredState desiredState) { } public virtual void ApplyDesiredStateToLive(TDesiredState desiredState) { } public virtual void Commit(TDesiredState desiredState) { } public virtual void HandleFailure(TDesiredState desiredState, bool liveStageFailed) { } } internal static class StandardBaselineDesiredStateCoordinator { internal static void Run(StandardDomainApplyPlan applyPlan, TDesiredState desiredState, StandardBaselineDesiredStateOperations operations) { Stopwatch stopwatch = Stopwatch.StartNew(); BaselineDesiredStateCapabilities capabilities = operations.Capabilities; StandardApplyStage standardApplyStage = StandardApplyStage.None; try { if ((capabilities & BaselineDesiredStateCapabilities.Validation) != BaselineDesiredStateCapabilities.None) { standardApplyStage = StandardApplyStage.Validate; operations.Validate(desiredState); } if ((capabilities & BaselineDesiredStateCapabilities.StaticBaseline) != BaselineDesiredStateCapabilities.None) { standardApplyStage = StandardApplyStage.RestoreStaticBaseline; operations.RestoreStaticBaseline(desiredState); } if ((capabilities & BaselineDesiredStateCapabilities.StaticApply) != BaselineDesiredStateCapabilities.None) { standardApplyStage = StandardApplyStage.ApplyStaticBaseline; operations.ApplyDesiredStateToStaticBaseline(desiredState); } if (!applyPlan.ShouldSkipLiveReload && applyPlan.NeedsLiveReload) { if ((capabilities & BaselineDesiredStateCapabilities.LiveBaseline) != BaselineDesiredStateCapabilities.None) { standardApplyStage = StandardApplyStage.PrepareLiveBaseline; operations.PrepareLiveBaseline(desiredState); } if ((capabilities & BaselineDesiredStateCapabilities.LiveApply) != BaselineDesiredStateCapabilities.None) { standardApplyStage = StandardApplyStage.ApplyLive; operations.ApplyDesiredStateToLive(desiredState); } } standardApplyStage = StandardApplyStage.Commit; operations.Commit(desiredState); stopwatch.Stop(); } catch (Exception ex) { stopwatch.Stop(); StandardApplyStage standardApplyStage2 = standardApplyStage; if (standardApplyStage2 == StandardApplyStage.ApplyStaticBaseline && (capabilities & BaselineDesiredStateCapabilities.StaticRollback) != BaselineDesiredStateCapabilities.None && (capabilities & BaselineDesiredStateCapabilities.StaticBaseline) != BaselineDesiredStateCapabilities.None) { try { operations.RestoreStaticBaseline(desiredState); } catch (Exception ex2) { DropNSpawnPlugin.DropNSpawnLogger.LogWarning((object)("Static rollback failed for domain '" + operations.DomainKey + "' after apply failure. " + ex2.Message)); DropNSpawnPlugin.DropNSpawnLogger.LogError((object)ex2); } } DropNSpawnPlugin.DropNSpawnLogger.LogWarning((object)$"Apply coordinator failed for domain '{operations.DomainKey}' at stage '{standardApplyStage2}' after {stopwatch.ElapsedMilliseconds} ms. {ex.Message}"); DropNSpawnPlugin.DropNSpawnLogger.LogError((object)ex); try { operations.HandleFailure(desiredState, standardApplyStage2 == StandardApplyStage.PrepareLiveBaseline || standardApplyStage2 == StandardApplyStage.ApplyLive); } catch (Exception ex3) { DropNSpawnPlugin.DropNSpawnLogger.LogWarning((object)("Failure handler failed for domain '" + operations.DomainKey + "'. " + ex3.Message)); DropNSpawnPlugin.DropNSpawnLogger.LogError((object)ex3); } } } } internal readonly struct StandardDomainApplyPlan { internal bool SameGameData { get; } internal bool PreviousDomainEnabled { get; } internal bool ShouldSkipLiveReload { get; } internal bool NeedsLiveReload { get; } internal HashSet? DirtyKeys { get; } internal StandardDomainApplyPlan(bool sameGameData, bool previousDomainEnabled, bool shouldSkipLiveReload, bool needsLiveReload, HashSet? dirtyKeys) { SameGameData = sameGameData; PreviousDomainEnabled = previousDomainEnabled; ShouldSkipLiveReload = shouldSkipLiveReload; NeedsLiveReload = needsLiveReload; DirtyKeys = dirtyKeys; } } internal static class StandardDomainApplySupport { internal static bool CanApplySynchronizedDomain(bool synchronizedPayloadReady) { return DropNSpawnPlugin.IsSourceOfTruth || synchronizedPayloadReady; } internal static bool IsAlreadyApplied(int? lastAppliedGameDataSignature, int currentGameDataSignature, bool? lastAppliedDomainEnabled, bool currentDomainEnabled, string lastAppliedConfigurationSignature, string currentConfigurationSignature) { if (lastAppliedGameDataSignature == currentGameDataSignature && lastAppliedDomainEnabled.HasValue && lastAppliedDomainEnabled.Value == currentDomainEnabled) { return string.Equals(lastAppliedConfigurationSignature, currentConfigurationSignature, StringComparison.Ordinal); } return false; } internal static bool IsAlreadyApplied(int? lastAppliedGameDataSignature, int currentGameDataSignature, bool? lastAppliedDomainEnabled, bool currentDomainEnabled, string lastAppliedConfigurationSignature, string currentConfigurationSignature, bool lastAppliedSynchronizedPayloadReady, bool currentSynchronizedPayloadReady) { if (IsAlreadyApplied(lastAppliedGameDataSignature, currentGameDataSignature, lastAppliedDomainEnabled, currentDomainEnabled, lastAppliedConfigurationSignature, currentConfigurationSignature)) { return lastAppliedSynchronizedPayloadReady == currentSynchronizedPayloadReady; } return false; } internal static StandardDomainApplyPlan BuildPlan(int? lastAppliedGameDataSignature, int currentGameDataSignature, bool? lastAppliedDomainEnabled, bool currentDomainEnabled, IReadOnlyDictionary lastAppliedEntrySignatures, IReadOnlyDictionary currentEntrySignatures, IReadOnlyDictionary emptyEntrySignatures, bool canUseTargetedLiveReload) { bool flag = lastAppliedGameDataSignature == currentGameDataSignature; bool valueOrDefault = lastAppliedDomainEnabled == true; IReadOnlyDictionary previous = (valueOrDefault ? lastAppliedEntrySignatures : emptyEntrySignatures); IReadOnlyDictionary readOnlyDictionary = (currentDomainEnabled ? currentEntrySignatures : emptyEntrySignatures); HashSet dirtyKeys = ((flag && canUseTargetedLiveReload) ? DomainDictionaryDiffSupport.BuildDirtyKeys(previous, readOnlyDictionary) : null); return new StandardDomainApplyPlan(flag, valueOrDefault, flag && lastAppliedDomainEnabled == false && !currentDomainEnabled, valueOrDefault || readOnlyDictionary.Count > 0, dirtyKeys); } } internal static class ConfigurationLoadSupport { internal sealed class LocalYamlDocument { internal string Path { get; set; } = ""; internal string? Yaml { get; set; } internal string? ReadError { get; set; } } internal sealed class ParsedLocalConfiguration { internal List Configuration { get; } internal List Warnings { get; } internal ParsedLocalConfiguration(List? configuration = null, List? warnings = null) { Configuration = configuration ?? new List(); Warnings = warnings ?? new List(); } } internal static List ReadLocalYamlDocuments(IEnumerable paths) { List list = new List(); foreach (string path in paths) { try { list.Add(new LocalYamlDocument { Path = path, Yaml = File.ReadAllText(path) }); } catch (Exception ex) { list.Add(new LocalYamlDocument { Path = path, ReadError = ex.GetType().Name + ": " + ex.Message }); } } return list; } internal static string BuildLocalPayload(IEnumerable documents) { StringBuilder stringBuilder = new StringBuilder(); foreach (LocalYamlDocument document in documents) { stringBuilder.Append(">>> ").Append(document.Path).AppendLine(); if (document.ReadError != null) { stringBuilder.Append("!read-error ").AppendLine(document.ReadError); } else { stringBuilder.Append(document.Yaml); } stringBuilder.AppendLine(); stringBuilder.AppendLine("<<<"); } return stringBuilder.ToString(); } internal static LocalLoadResult ParseLocalConfigurationDocuments(List documents, Func> parseDocument, Func, string, List, List> prepareConfiguration, Func formatExceptionLocation, string parseFailureHint) { List list = new List(); List list2 = new List(); List list3 = new List(); int num = 0; int num2 = 0; foreach (LocalYamlDocument document in documents) { if (document.ReadError != null) { list2.Add("Failed to read " + document.Path + ". " + document.ReadError); continue; } try { ParsedLocalConfiguration parsedLocalConfiguration = parseDocument(document.Yaml ?? "", document.Path); list3.AddRange(parsedLocalConfiguration.Warnings); num += parsedLocalConfiguration.Configuration.Count; list.AddRange(prepareConfiguration(parsedLocalConfiguration.Configuration, document.Path, list3)); num2++; } catch (Exception ex) { list2.Add($"Failed to parse {document.Path}{formatExceptionLocation(ex)}. {parseFailureHint} {ex}"); } } return new LocalLoadResult { Entries = list, Errors = list2, Warnings = list3, ParsedEntryCount = num, LoadedFileCount = num2 }; } } internal static class CreatureManagerSpawnReferenceSupport { private enum ProviderKind { CreatureManagerTemplate, JotunnCreatureManager } internal sealed class ReferenceSnapshot { public List Projections { get; set; } = new List(); public string Signature { get; set; } = ""; } internal sealed class ReferenceProjection { public CanonicalSpawnSystemEntry Entry { get; set; } public string SignatureToken { get; set; } = ""; public string SourceKey { get; set; } = ""; } private sealed class ProviderHandle { public ProviderKind Kind { get; set; } public Assembly Assembly { get; set; } public string AssemblyName { get; set; } = ""; public Type CreatureType { get; set; } public Type? ManagerType { get; set; } public Type? InternalNameAttributeType { get; set; } public PropertyInfo? ManagerInstanceProperty { get; set; } public FieldInfo? ManagerCreaturesField { get; set; } public PropertyInfo? ManagerCreaturesProperty { get; set; } public FieldInfo? RegisteredCreaturesField { get; set; } public FieldInfo? CreatureConfigsField { get; set; } } private enum SpawnMode { Default, Custom, Disabled } private static readonly object Sync = new object(); private static readonly ISerializer SignatureSerializer = new SerializerBuilder().WithNamingConvention(CamelCaseNamingConvention.Instance).ConfigureDefaultValuesHandling(DefaultValuesHandling.OmitNull | DefaultValuesHandling.OmitDefaults).Build(); private static string _providerAssemblySignature = ""; private static List _providerCache = new List(); private static string _snapshotAssemblySignature = ""; private static ReferenceSnapshot? _snapshotCache; internal static ReferenceSnapshot GetReferenceSnapshot(bool forceRefresh = false) { lock (Sync) { Assembly[] array = (from assembly in AppDomain.CurrentDomain.GetAssemblies() where !assembly.IsDynamic select assembly).ToArray(); string text = ReferenceRefreshSupport.ComputeStableHashForKeys(array.Select((Assembly assembly) => assembly.FullName ?? assembly.GetName().Name ?? "")); List providers = GetProviders(array, text); if (!forceRefresh && _snapshotCache != null && string.Equals(_snapshotAssemblySignature, text, StringComparison.Ordinal)) { return _snapshotCache; } List list = new List(); foreach (ProviderHandle item in providers) { list.AddRange(CollectReferenceProjections(item)); } ReferenceSnapshot obj = new ReferenceSnapshot { Projections = list, Signature = ReferenceRefreshSupport.ComputeStableHashForKeys(list.Select((ReferenceProjection projection) => projection.SignatureToken)) }; _snapshotAssemblySignature = text; _snapshotCache = obj; return obj; } } private static List GetProviders(Assembly[] assemblies, string assemblySignature) { if (string.Equals(_providerAssemblySignature, assemblySignature, StringComparison.Ordinal)) { return _providerCache; } List list = new List(); foreach (Assembly assembly in assemblies) { Type type = SafeGetType(assembly, "CreatureManager.Creature"); if (!(type == null)) { FieldInfo fieldInfo = TryGetTypeField(type, "registeredCreatures"); if (!(fieldInfo == null)) { list.Add(new ProviderHandle { Kind = ProviderKind.CreatureManagerTemplate, Assembly = assembly, AssemblyName = (assembly.GetName().Name ?? assembly.FullName ?? "Unknown / Untracked"), CreatureType = type, InternalNameAttributeType = SafeGetType(assembly, "CreatureManager.InternalName"), RegisteredCreaturesField = fieldInfo, CreatureConfigsField = TryGetTypeField(type, "creatureConfigs") }); } } } ProviderHandle providerHandle = TryCreateJotunnProvider(assemblies); if (providerHandle != null) { list.Add(providerHandle); } _providerAssemblySignature = assemblySignature; _providerCache = list; return _providerCache; } private static List CollectReferenceProjections(ProviderHandle provider) { if (provider.Kind != ProviderKind.JotunnCreatureManager) { return CollectCreatureManagerTemplateReferenceProjections(provider); } return CollectJotunnReferenceProjections(provider); } private static ProviderHandle? TryCreateJotunnProvider(IEnumerable assemblies) { foreach (Assembly assembly in assemblies) { Type type = SafeGetType(assembly, "Jotunn.Managers.CreatureManager"); Type type2 = SafeGetType(assembly, "Jotunn.Entities.CustomCreature"); if (!(type == null) && !(type2 == null)) { PropertyInfo propertyInfo = TryGetTypeProperty(type, "Instance"); FieldInfo fieldInfo = TryGetTypeField(type, "Creatures"); PropertyInfo propertyInfo2 = TryGetTypeProperty(type, "Creatures"); if (!(propertyInfo == null) && (!(fieldInfo == null) || !(propertyInfo2 == null))) { return new ProviderHandle { Kind = ProviderKind.JotunnCreatureManager, Assembly = assembly, AssemblyName = (assembly.GetName().Name ?? assembly.FullName ?? "Unknown / Untracked"), CreatureType = type2, ManagerType = type, ManagerInstanceProperty = propertyInfo, ManagerCreaturesField = fieldInfo, ManagerCreaturesProperty = propertyInfo2 }; } } } return null; } private static List CollectJotunnReferenceProjections(ProviderHandle provider) { List list = new List(); object obj = provider.ManagerInstanceProperty?.GetValue(null, null); if (obj == null) { return list; } foreach (object item in GetEnumerable(provider.ManagerCreaturesField?.GetValue(obj) ?? provider.ManagerCreaturesProperty?.GetValue(obj, null))) { if (item == null || !TryGetRawMemberValue(item, "Prefab", out object value)) { continue; } GameObject val = (GameObject)((value is GameObject) ? value : null); if (val == null || string.IsNullOrWhiteSpace(((Object)val).name)) { continue; } string text = ResolveJotunnSourceKey(item, provider.AssemblyName); IEnumerable enumerable = Enumerable.Empty(); if (TryGetRawMemberValue(item, "Spawns", out object value2)) { enumerable = GetEnumerable(value2); } foreach (object item2 in enumerable) { SpawnData val2 = (SpawnData)((item2 is SpawnData) ? item2 : null); if (val2 != null) { CanonicalSpawnSystemEntry canonicalSpawnSystemEntry = SpawnSystemManager.CreateReferenceEntryForExternalProjection(val2, ((Object)val).name); if (!string.IsNullOrWhiteSpace(canonicalSpawnSystemEntry.Prefab)) { canonicalSpawnSystemEntry.ReferenceOwnerName = text; list.Add(new ReferenceProjection { Entry = canonicalSpawnSystemEntry, SignatureToken = text + ":" + SignatureSerializer.Serialize(canonicalSpawnSystemEntry).TrimEnd('\r', '\n'), SourceKey = text }); } } } } return list; } private static string ResolveJotunnSourceKey(object creature, string fallback) { if (TryGetRawMemberValue(creature, "SourceMod", out object value) && value != null) { if (TryGetRawMemberValue(value, "GUID", out object value2)) { string text = NormalizeOptionalString(value2?.ToString()); string text2 = ResolvePluginOwnerName(text); if (!string.IsNullOrWhiteSpace(text2)) { return text2; } if (!string.IsNullOrWhiteSpace(text)) { return text; } } if (TryGetRawMemberValue(value, "Name", out object value3)) { string text3 = NormalizeOptionalString(value3?.ToString()); if (!string.IsNullOrWhiteSpace(text3)) { return text3; } } } return fallback; } private static string? ResolvePluginOwnerName(string? pluginGuid) { string text = NormalizeOptionalString(pluginGuid) ?? ""; if (text.Length == 0) { return null; } if (Chainloader.PluginInfos.TryGetValue(text, out var value)) { string text2 = NormalizeOptionalString(value.Metadata.Name) ?? ""; if (text2.Length > 0) { return text2; } return text; } return null; } private static List CollectCreatureManagerTemplateReferenceProjections(ProviderHandle provider) { List list = new List(); IEnumerable enumerable = GetEnumerable(provider.RegisteredCreaturesField?.GetValue(null)); IDictionary dictionary = provider.CreatureConfigsField?.GetValue(null) as IDictionary; foreach (object item in enumerable) { if (item == null || !TryGetRawMemberValue(item, "Prefab", out object value)) { continue; } GameObject val = (GameObject)((value is GameObject) ? value : null); if (val == null || string.IsNullOrWhiteSpace(((Object)val).name)) { continue; } object creatureConfig = null; if (dictionary != null && dictionary.Contains(item)) { creatureConfig = dictionary[item]; } SpawnMode spawnMode = ResolveSpawnMode(creatureConfig); if (spawnMode != SpawnMode.Disabled && GetBoolEffective(item, creatureConfig, spawnMode, "CanSpawn", fallbackValue: true)) { CanonicalSpawnSystemEntry canonicalSpawnSystemEntry = BuildReferenceEntry(provider, ((Object)val).name, item, creatureConfig, spawnMode); if (!string.IsNullOrWhiteSpace(canonicalSpawnSystemEntry.Prefab)) { canonicalSpawnSystemEntry.ReferenceOwnerName = provider.AssemblyName; list.Add(new ReferenceProjection { Entry = canonicalSpawnSystemEntry, SignatureToken = provider.AssemblyName + ":" + SignatureSerializer.Serialize(canonicalSpawnSystemEntry).TrimEnd('\r', '\n'), SourceKey = provider.AssemblyName }); } } } return list; } private static CanonicalSpawnSystemEntry BuildReferenceEntry(ProviderHandle provider, string prefabName, object creature, object? creatureConfig, SpawnMode spawnMode) { //IL_01ce: Unknown result type (might be due to invalid IL or missing references) //IL_01d3: Unknown result type (might be due to invalid IL or missing references) //IL_01d5: Unknown result type (might be due to invalid IL or missing references) //IL_01da: Unknown result type (might be due to invalid IL or missing references) CanonicalSpawnSystemEntry obj = new CanonicalSpawnSystemEntry { Prefab = prefabName, Enabled = true }; SpawnSystemSpawnDefinition spawnSystemSpawnDefinition = new SpawnSystemSpawnDefinition(); bool flag = false; if (TryGetFloatEffective(creature, creatureConfig, spawnMode, "CheckSpawnInterval", out var value)) { spawnSystemSpawnDefinition.SpawnInterval = value; flag = true; } if (TryGetFloatEffective(creature, creatureConfig, spawnMode, "SpawnChance", out var value2)) { spawnSystemSpawnDefinition.SpawnChance = value2; flag = true; } if (TryGetFloatRangeEffective(creature, creatureConfig, spawnMode, "GroupSize", out var min, out var max)) { spawnSystemSpawnDefinition.GroupSize = RangeFormatting.From((int)Math.Round(min), (int)Math.Round(max)); flag = true; } if (TryGetFloatEffective(creature, creatureConfig, spawnMode, "SpawnAltitude", out var value3)) { spawnSystemSpawnDefinition.GroundOffset = value3; flag = true; } if (GetBoolEffective(creature, creatureConfig, spawnMode, "AttackImmediately", fallbackValue: false)) { spawnSystemSpawnDefinition.HuntPlayer = true; flag = true; } if (!GetBoolEffective(creature, creatureConfig, spawnMode, "CanHaveStars", fallbackValue: true)) { spawnSystemSpawnDefinition.Level = RangeFormatting.From(1, 1); flag = true; } if (TryGetFloatEffective(creature, creatureConfig, spawnMode, "Maximum", out var value4)) { spawnSystemSpawnDefinition.MaxSpawned = (int)Math.Round(value4); flag = true; } if (TryGetFloatRangeEffective(creature, creatureConfig, spawnMode, "RequiredAltitude", out var min2, out var max2)) { spawnSystemSpawnDefinition.Altitude = RangeFormatting.From(min2, max2); flag = true; } if (TryGetFloatRangeEffective(creature, creatureConfig, spawnMode, "RequiredOceanDepth", out var min3, out var max3)) { spawnSystemSpawnDefinition.OceanDepth = RangeFormatting.From(min3, max3); flag = true; } if (TryGetEffectiveMemberValue(creature, creatureConfig, spawnMode, "SpecificSpawnTime", out object value5)) { TimeOfDayDefinition timeOfDayDefinition = ConvertTimeOfDayDefinition(value5); if (timeOfDayDefinition != null) { spawnSystemSpawnDefinition.TimeOfDay = timeOfDayDefinition; flag = true; } } if (TryGetEffectiveMemberValue(creature, creatureConfig, spawnMode, "Biome", out object value6) && value6 is Biome val && (int)val != 0) { spawnSystemSpawnDefinition.Biomes = ConvertBiomes(val); flag = true; } if (TryGetEffectiveMemberValue(creature, creatureConfig, spawnMode, "SpecificSpawnArea", out object value7)) { List list = ConvertBiomeAreas(value7); if (list != null) { spawnSystemSpawnDefinition.BiomeAreas = list; flag = true; } } if (TryGetEffectiveMemberValue(creature, creatureConfig, spawnMode, "RequiredWeather", out object value8)) { List list2 = ConvertRequiredEnvironments(provider, value8); if (list2 != null && list2.Count > 0) { spawnSystemSpawnDefinition.RequiredEnvironments = list2; flag = true; } } if (TryGetEffectiveMemberValue(creature, creatureConfig, spawnMode, "RequiredGlobalKey", out object value9)) { string text = ConvertInternalName(provider, value9); if (!string.IsNullOrWhiteSpace(text)) { spawnSystemSpawnDefinition.RequiredGlobalKey = text; flag = true; } } if (TryGetEffectiveMemberValue(creature, creatureConfig, spawnMode, "ForestSpawn", out object value10)) { bool? inForest = ConvertForestToggle(value10); if (inForest.HasValue) { spawnSystemSpawnDefinition.InForest = inForest; flag = true; } } if (TryGetEffectiveMemberValue(creature, creatureConfig, spawnMode, "CreatureFaction", out object value11)) { string text2 = NormalizeOptionalString(value11?.ToString()); if (!string.IsNullOrWhiteSpace(text2)) { spawnSystemSpawnDefinition.Faction = text2; flag = true; } } obj.SpawnSystem = (flag ? spawnSystemSpawnDefinition : null); return obj; } private static SpawnMode ResolveSpawnMode(object? creatureConfig) { if (!TryGetWrappedMemberValue(creatureConfig, "Spawn", out object value)) { return SpawnMode.Default; } string text = NormalizeOptionalString(value?.ToString()) ?? ""; if (text.Equals("Disabled", StringComparison.OrdinalIgnoreCase)) { return SpawnMode.Disabled; } if (text.Equals("Custom", StringComparison.OrdinalIgnoreCase)) { return SpawnMode.Custom; } return SpawnMode.Default; } private static IEnumerable GetEnumerable(object? value) { if (value is string || !(value is IEnumerable result)) { return Array.Empty(); } return result; } private static bool TryGetEffectiveMemberValue(object creature, object? creatureConfig, SpawnMode spawnMode, string memberName, out object? value) { value = null; if (spawnMode == SpawnMode.Custom && TryGetWrappedMemberValue(creatureConfig, memberName, out value)) { return true; } if (TryGetMemberValue(creature, memberName, out value)) { return true; } if (spawnMode != SpawnMode.Default && TryGetWrappedMemberValue(creatureConfig, memberName, out value)) { return true; } value = null; return false; } private static bool GetBoolEffective(object creature, object? creatureConfig, SpawnMode spawnMode, string memberName, bool fallbackValue) { if (!TryGetEffectiveMemberValue(creature, creatureConfig, spawnMode, memberName, out object value) || !TryConvertToBool(value, out var parsed)) { return fallbackValue; } return parsed; } private static bool TryGetFloatEffective(object creature, object? creatureConfig, SpawnMode spawnMode, string memberName, out float value) { if (TryGetEffectiveMemberValue(creature, creatureConfig, spawnMode, memberName, out object value2) && TryConvertToFloat(value2, out value)) { return true; } value = 0f; return false; } private static bool TryGetFloatRangeEffective(object creature, object? creatureConfig, SpawnMode spawnMode, string memberName, out float min, out float max) { if (TryGetEffectiveMemberValue(creature, creatureConfig, spawnMode, memberName, out object value) && TryReadRange(value, out min, out max)) { return true; } min = 0f; max = 0f; return false; } private static bool TryReadRange(object? value, out float min, out float max) { min = 0f; max = 0f; if (value == null) { return false; } value.GetType(); if (!TryGetRawMemberValue(value, "min", out object value2) || !TryGetRawMemberValue(value, "max", out object value3) || !TryConvertToFloat(value2, out min) || !TryConvertToFloat(value3, out max)) { return false; } return true; } private static bool TryGetWrappedMemberValue(object? instance, string memberName, out object? value) { value = null; if (!TryGetRawMemberValue(instance, memberName, out object value2)) { return false; } value = UnwrapConfigValue(value2); return true; } private static bool TryGetMemberValue(object? instance, string memberName, out object? value) { value = null; if (!TryGetRawMemberValue(instance, memberName, out object value2)) { return false; } value = UnwrapConfigValue(value2); return true; } private static bool TryGetRawMemberValue(object? instance, string memberName, out object? value) { value = null; if (instance == null || string.IsNullOrWhiteSpace(memberName)) { return false; } Type type = instance.GetType(); FieldInfo fieldInfo = type.GetField(memberName, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic) ?? type.GetFields(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault((FieldInfo candidate) => string.Equals(candidate.Name, memberName, StringComparison.OrdinalIgnoreCase)); if (fieldInfo != null) { value = fieldInfo.GetValue(instance); return true; } PropertyInfo propertyInfo = type.GetProperty(memberName, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic) ?? type.GetProperties(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault((PropertyInfo candidate) => string.Equals(candidate.Name, memberName, StringComparison.OrdinalIgnoreCase)); if (propertyInfo == null || !propertyInfo.CanRead) { return false; } value = propertyInfo.GetValue(instance, null); return true; } private static object? UnwrapConfigValue(object? value) { object obj = value; for (int i = 0; i < 4; i++) { if (obj == null) { break; } if (obj is string || obj.GetType().IsPrimitive || obj.GetType().IsEnum) { return obj; } ConfigEntryBase val = (ConfigEntryBase)((obj is ConfigEntryBase) ? obj : null); if (val != null) { return val.BoxedValue; } MethodInfo method = obj.GetType().GetMethod("get", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null); if (method != null && method.ReturnType != typeof(void)) { obj = method.Invoke(obj, null); continue; } PropertyInfo property = obj.GetType().GetProperty("BoxedValue", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (property != null && property.CanRead) { obj = property.GetValue(obj, null); continue; } PropertyInfo property2 = obj.GetType().GetProperty("Value", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (!(property2 != null) || !property2.CanRead) { break; } obj = property2.GetValue(obj, null); } return obj; } private static bool TryConvertToBool(object? value, out bool parsed) { if (!(value is bool flag)) { if (value is string value2 && bool.TryParse(value2, out var result)) { parsed = result; return true; } parsed = false; return false; } bool flag2 = flag; parsed = flag2; return true; } private static bool TryConvertToFloat(object? value, out float parsed) { if (!(value is byte b)) { if (!(value is short num)) { if (!(value is int num2)) { if (!(value is long num3)) { if (!(value is float num4)) { if (!(value is double num5)) { if (!(value is decimal num6)) { if (value is string s && float.TryParse(s, NumberStyles.Float, CultureInfo.InvariantCulture, out var result)) { parsed = result; return true; } parsed = 0f; return false; } decimal num7 = num6; parsed = (float)num7; return true; } double num8 = num5; parsed = (float)num8; return true; } float num9 = num4; parsed = num9; return true; } long num10 = num3; parsed = num10; return true; } int num11 = num2; parsed = num11; return true; } short num12 = num; parsed = num12; return true; } byte b2 = b; parsed = (int)b2; return true; } private static TimeOfDayDefinition? ConvertTimeOfDayDefinition(object? value) { string text = NormalizeOptionalString(value?.ToString()) ?? ""; if (text.Length == 0) { return null; } if (text.Equals("Day", StringComparison.OrdinalIgnoreCase)) { return new TimeOfDayDefinition { Values = new List { "day" } }; } if (text.Equals("Night", StringComparison.OrdinalIgnoreCase)) { return new TimeOfDayDefinition { Values = new List { "night" } }; } if (text.Equals("Always", StringComparison.OrdinalIgnoreCase)) { return new TimeOfDayDefinition { Values = new List { "day", "night" } }; } return null; } private static List? ConvertBiomeAreas(object? value) { string text = NormalizeOptionalString(value?.ToString()) ?? ""; if (text.Length == 0) { return null; } if (text.Equals("Everywhere", StringComparison.OrdinalIgnoreCase)) { return new List { "Everything" }; } if (text.Equals("Center", StringComparison.OrdinalIgnoreCase)) { return new List { "Median" }; } if (text.Equals("Edge", StringComparison.OrdinalIgnoreCase)) { return new List { "Edge" }; } return null; } private static bool? ConvertForestToggle(object? value) { string text = NormalizeOptionalString(value?.ToString()) ?? ""; if (text.Length == 0) { return null; } if (text.Equals("Yes", StringComparison.OrdinalIgnoreCase)) { return true; } if (text.Equals("No", StringComparison.OrdinalIgnoreCase)) { return false; } return null; } private static List ConvertBiomes(Biome biomes) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) return BiomeResolutionSupport.ConvertBiomeMaskToNames(biomes); } private static List? ConvertRequiredEnvironments(ProviderHandle provider, object? value) { if (value == null || !value.GetType().IsEnum) { return null; } List list = new List(); ulong num = Convert.ToUInt64(value, CultureInfo.InvariantCulture); FieldInfo[] fields = value.GetType().GetFields(BindingFlags.Static | BindingFlags.Public); foreach (FieldInfo fieldInfo in fields) { object value2 = fieldInfo.GetValue(null); if (value2 == null) { continue; } ulong num2 = Convert.ToUInt64(value2, CultureInfo.InvariantCulture); if (num2 != 0L && (num & num2) == num2) { string internalName = GetInternalName(provider, fieldInfo); if (!string.IsNullOrWhiteSpace(internalName)) { list.Add(internalName); } } } if (list.Count != 0) { return list.Distinct(StringComparer.OrdinalIgnoreCase).ToList(); } return null; } private static string? ConvertInternalName(ProviderHandle provider, object? value) { if (value == null || !value.GetType().IsEnum) { return NormalizeOptionalString(value?.ToString()); } string text = value.ToString(); if (string.IsNullOrWhiteSpace(text)) { return null; } FieldInfo field = value.GetType().GetField(text, BindingFlags.Static | BindingFlags.Public); if (field == null) { return null; } return NormalizeOptionalString(GetInternalName(provider, field)); } private static string? GetInternalName(ProviderHandle provider, MemberInfo member) { if (provider.InternalNameAttributeType == null) { return member.Name; } object obj = member.GetCustomAttributes(provider.InternalNameAttributeType, inherit: false).FirstOrDefault(); if (obj == null) { return member.Name; } if (TryGetRawMemberValue(obj, "internalName", out object value)) { return NormalizeOptionalString(value?.ToString()); } return member.Name; } private static string? NormalizeOptionalString(string? value) { string text = (value ?? "").Trim(); if (text.Length != 0) { return text; } return null; } private static Type? SafeGetType(Assembly assembly, string fullTypeName) { try { return assembly.GetType(fullTypeName, throwOnError: false, ignoreCase: false); } catch { return null; } } private static PropertyInfo? TryGetTypeProperty(Type? type, string memberName) { if (type == null || string.IsNullOrWhiteSpace(memberName)) { return null; } Type type2 = type; while (type2 != null) { PropertyInfo propertyInfo = type2.GetProperty(memberName, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic) ?? type2.GetProperties(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault((PropertyInfo candidate) => string.Equals(candidate.Name, memberName, StringComparison.OrdinalIgnoreCase)); if (propertyInfo != null) { return propertyInfo; } type2 = type2.BaseType; } return null; } private static FieldInfo? TryGetTypeField(Type? type, string memberName) { if (type == null || string.IsNullOrWhiteSpace(memberName)) { return null; } Type type2 = type; while (type2 != null) { FieldInfo fieldInfo = type2.GetField(memberName, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic) ?? type2.GetFields(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault((FieldInfo candidate) => string.Equals(candidate.Name, memberName, StringComparison.OrdinalIgnoreCase)); if (fieldInfo != null) { return fieldInfo; } type2 = type2.BaseType; } return null; } } internal static class BiomeResolutionSupport { private static readonly Dictionary VanillaBiomeLookup = BuildVanillaBiomeLookup(); private static readonly Type? ExpandWorldDataBiomeManagerType = Type.GetType("ExpandWorldData.BiomeManager, ExpandWorldData"); private static readonly Type? ExpandWorldDataDataManagerType = Type.GetType("ExpandWorldData.DataManager, ExpandWorldData"); private static readonly FieldInfo? ExpandWorldDataConfigSyncField = Type.GetType("ExpandWorldData.EWD, ExpandWorldData")?.GetField("ConfigSync", BindingFlags.Static | BindingFlags.Public); private static readonly PropertyInfo? ExpandWorldDataIsReadyProperty = ExpandWorldDataDataManagerType?.GetProperty("IsReady", BindingFlags.Static | BindingFlags.Public); private static readonly PropertyInfo? ExpandWorldDataIsSourceOfTruthProperty = ExpandWorldDataConfigSyncField?.FieldType.GetProperty("IsSourceOfTruth", BindingFlags.Instance | BindingFlags.Public); private static readonly PropertyInfo? ExpandWorldDataInitialSyncDoneProperty = ExpandWorldDataConfigSyncField?.FieldType.GetProperty("InitialSyncDone", BindingFlags.Instance | BindingFlags.Public); private static readonly MethodInfo? ExpandWorldDataTryGetBiomeMethod = Type.GetType("ExpandWorldData.BiomeManager, ExpandWorldData")?.GetMethod("TryGetBiome", BindingFlags.Static | BindingFlags.Public, null, new Type[2] { typeof(string), typeof(Biome).MakeByRefType() }, null); private static readonly MethodInfo? ExpandWorldDataTryGetDisplayNameMethod = ExpandWorldDataBiomeManagerType?.GetMethod("TryGetDisplayName", BindingFlags.Static | BindingFlags.Public, null, new Type[2] { typeof(Biome), typeof(string).MakeByRefType() }, null); private static readonly FieldInfo? ExpandWorldDataBiomeToDisplayNameField = ExpandWorldDataBiomeManagerType?.GetField("BiomeToDisplayName", BindingFlags.Static | BindingFlags.Public); internal static bool TryResolveBiomeToken(string? configuredBiome, out Biome biome) { //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Expected I4, but got Unknown string text = (configuredBiome ?? "").Trim(); if (text.Length == 0) { biome = (Biome)0; return false; } if (string.Equals(text, "All", StringComparison.OrdinalIgnoreCase)) { biome = (Biome)(int)GetKnownBiomeMask(); return true; } if (int.TryParse(text, NumberStyles.Integer, CultureInfo.InvariantCulture, out var result) && result != 0) { biome = (Biome)result; return true; } if (Enum.TryParse(text, ignoreCase: true, out biome)) { return true; } if (VanillaBiomeLookup.TryGetValue(NormalizeBiomeToken(text), out biome)) { return true; } if (TryResolveExpandWorldDataBiome(text, out biome)) { return true; } biome = (Biome)0; return false; } internal static bool MatchesBiome(Biome currentBiome, string? configuredBiome) { //IL_000a: 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_000c: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Invalid comparison between Unknown and I4 if (TryResolveBiomeToken(configuredBiome, out var biome)) { return (currentBiome & biome) > 0; } return false; } internal static bool TryResolveBiomeMask(IEnumerable? configuredBiomes, out Biome biomeMask) { //IL_004b: 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_004e: Expected I4, but got Unknown biomeMask = (Biome)0; bool result = false; foreach (string item in configuredBiomes ?? Array.Empty()) { string text = (item ?? "").Trim(); if (text.Length != 0) { result = true; if (!TryResolveBiomeToken(text, out var biome)) { biomeMask = (Biome)0; return false; } biomeMask |= biome; } } return result; } internal static Biome? ResolveBiomeMaskOrNull(IEnumerable? configuredBiomes) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) if (!TryResolveBiomeMask(configuredBiomes, out var biomeMask)) { return null; } return biomeMask; } internal static string GetBiomeDisplayName(Biome biome) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Expected I4, but got Unknown if (TryGetExpandWorldDataBiomeDisplayName(biome, out string displayName)) { return displayName; } return Enum.GetName(typeof(Biome), biome) ?? ((int)biome).ToString(CultureInfo.InvariantCulture); } internal static Biome GetKnownBiomeMask() { //IL_0005: 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_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_0012: Unknown result type (might be due to invalid IL or missing references) Biome val = (Biome)895; if (TryGetExpandWorldDataKnownBiomeMask(out var mask)) { val |= mask; } return val; } internal static bool IsKnownAllBiomeMask(Biome biomes) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Invalid comparison between Unknown and I4 //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) if ((int)biomes != 895) { return biomes == GetKnownBiomeMask(); } return true; } internal static List ConvertBiomeMaskToNames(Biome biomes) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Expected I4, but got Unknown //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Expected I4, but got Unknown //IL_004f: Unknown result type (might be due to invalid IL or missing references) if ((int)biomes == 0) { return new List(); } if (IsKnownAllBiomeMask(biomes)) { return new List { "All" }; } List list = new List(); uint num = (uint)(int)biomes; foreach (Biome knownBiomeValue in GetKnownBiomeValues()) { uint num2 = (uint)(int)knownBiomeValue; if (num2 != 0 && (num & num2) == num2) { list.Add(GetBiomeDisplayName(knownBiomeValue)); num &= ~num2; } } AppendRemainingBiomeBits(list, num); return list; } internal static bool IsExpandWorldDataPresent() { if (!(ExpandWorldDataTryGetBiomeMethod != null)) { return ExpandWorldDataIsReadyProperty != null; } return true; } internal static bool IsExpandWorldDataReadyOrUnavailable() { if (ExpandWorldDataIsReadyProperty != null) { try { if (ExpandWorldDataIsReadyProperty.GetValue(null) is bool result) { return result; } } catch { } } if (ExpandWorldDataConfigSyncField == null || ExpandWorldDataIsSourceOfTruthProperty == null || ExpandWorldDataInitialSyncDoneProperty == null) { return true; } try { object value = ExpandWorldDataConfigSyncField.GetValue(null); if (value == null) { return true; } if (!(ExpandWorldDataIsSourceOfTruthProperty.GetValue(value) is bool flag) || !(ExpandWorldDataInitialSyncDoneProperty.GetValue(value) is bool flag2)) { return true; } return flag || flag2; } catch { return true; } } internal static bool ShouldWaitForExpandWorldDataBiomeResolution(IEnumerable? configuredBiomes, Biome? resolvedBiomeMask) { if (!IsExpandWorldDataPresent() || IsExpandWorldDataReadyOrUnavailable()) { return false; } if (ContainsAllBiomeToken(configuredBiomes)) { return true; } if (resolvedBiomeMask.HasValue) { return false; } Biome biomeMask; return !TryResolveBiomeMask(configuredBiomes, out biomeMask); } internal static string NormalizeBiomeToken(string? value) { StringBuilder stringBuilder = new StringBuilder(); string text = (value ?? "").Trim(); foreach (char c in text) { if (char.IsLetterOrDigit(c)) { stringBuilder.Append(char.ToLowerInvariant(c)); } } return stringBuilder.ToString(); } private static Dictionary BuildVanillaBiomeLookup() { //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_0041: Unknown result type (might be due to invalid IL or missing references) Dictionary dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); foreach (Biome value in Enum.GetValues(typeof(Biome))) { dictionary[NormalizeBiomeToken(((object)value/*cast due to .constrained prefix*/).ToString())] = value; } dictionary["ashlands"] = (Biome)32; return dictionary; } private static bool TryResolveExpandWorldDataBiome(string configuredBiome, out Biome biome) { //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_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Expected I4, but got Unknown if (ExpandWorldDataTryGetBiomeMethod == null) { biome = (Biome)0; return false; } object[] array = new object[2] { configuredBiome, (object)(Biome)0 }; object obj = ExpandWorldDataTryGetBiomeMethod.Invoke(null, array); bool flag = default(bool); int num; if (obj is bool) { flag = (bool)obj; num = 1; } else { num = 0; } if (((uint)num & (flag ? 1u : 0u)) != 0 && array[1] is Biome val) { biome = (Biome)(int)val; return true; } biome = (Biome)0; return false; } private static bool TryGetExpandWorldDataBiomeDisplayName(Biome biome, out string displayName) { //IL_001e: Unknown result type (might be due to invalid IL or missing references) displayName = ""; if (ExpandWorldDataTryGetDisplayNameMethod == null) { return false; } object[] array = new object[2] { biome, null }; object obj = ExpandWorldDataTryGetDisplayNameMethod.Invoke(null, array); bool flag = default(bool); int num; if (obj is bool) { flag = (bool)obj; num = 1; } else { num = 0; } if (((uint)num & (flag ? 1u : 0u)) != 0 && array[1] is string text && !string.IsNullOrWhiteSpace(text)) { displayName = text; return true; } return false; } private static List GetKnownBiomeValues() { //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0031: 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_006e: Unknown result type (might be due to invalid IL or missing references) List list = new List(); HashSet seen = new HashSet(); foreach (Biome value in Enum.GetValues(typeof(Biome))) { AddKnownBiomeValue(list, seen, value); } foreach (Biome expandWorldDataKnownBiomeValue in GetExpandWorldDataKnownBiomeValues()) { AddKnownBiomeValue(list, seen, expandWorldDataKnownBiomeValue); } return list; } private static void AddKnownBiomeValue(List values, HashSet seen, Biome biome) { //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_0009: Invalid comparison between Unknown and I4 //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Expected I4, but got Unknown //IL_001c: Unknown result type (might be due to invalid IL or missing references) if ((int)biome != 0 && (int)biome != 895) { int num = (int)biome; if (num != 0 && seen.Add(num)) { values.Add(biome); } } } private static IEnumerable GetExpandWorldDataKnownBiomeValues() { if (!TryGetExpandWorldDataBiomeDictionary(out IDictionary biomeDictionary)) { yield break; } foreach (DictionaryEntry item in biomeDictionary) { object key = item.Key; if (key is Biome) { yield return (Biome)key; } } } private static bool TryGetExpandWorldDataKnownBiomeMask(out Biome mask) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0018: 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_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Invalid comparison between Unknown and I4 //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Expected I4, but got Unknown mask = (Biome)0; bool result = false; foreach (Biome expandWorldDataKnownBiomeValue in GetExpandWorldDataKnownBiomeValues()) { if ((int)expandWorldDataKnownBiomeValue != 0 && (int)expandWorldDataKnownBiomeValue != 895) { mask |= expandWorldDataKnownBiomeValue; result = true; } } return result; } private static bool TryGetExpandWorldDataBiomeDictionary(out IDictionary? biomeDictionary) { biomeDictionary = null; if (ExpandWorldDataBiomeToDisplayNameField == null) { return false; } try { biomeDictionary = ExpandWorldDataBiomeToDisplayNameField.GetValue(null) as IDictionary; return biomeDictionary != null; } catch { return false; } } private static bool ContainsAllBiomeToken(IEnumerable? configuredBiomes) { foreach (string item in configuredBiomes ?? Array.Empty()) { if (string.Equals((item ?? "").Trim(), "All", StringComparison.OrdinalIgnoreCase)) { return true; } } return false; } private static void AppendRemainingBiomeBits(List values, uint remainingMask) { if (remainingMask == 0) { return; } uint num = 1u; while (num != 0 && num <= remainingMask) { if ((remainingMask & num) != 0) { values.Add(GetBiomeDisplayName((Biome)num)); } num <<= 1; } } } internal static class ConditionDialectSupport { private const string CharacterDropLevelReason = "level filters are only valid for character-drop conditions"; private const string CharacterDropStateReason = "state filters are only valid for character-drop conditions"; private const string CharacterDropFactionReason = "faction filters are only valid for character-drop conditions"; internal static void StripUnsupportedObjectDropFields(ConditionsDefinition? conditions, string context, Action warn) { if (conditions != null) { StripLevel(conditions, context, "conditions", "level filters are only valid for character-drop conditions", warn); StripStates(conditions, context, "conditions", "state filters are only valid for character-drop conditions", warn); StripFactions(conditions, context, "conditions", "faction filters are only valid for character-drop conditions", warn); } } internal static void StripUnsupportedSpawnerTargetFields(ConditionsDefinition? conditions, string context, bool allowCreatureSpawnerRuntimeOverlapKeys, Action warn) { if (conditions != null) { StripLocations(conditions, context, "conditions", "spawner entries use the top-level locations selector", warn); StripLevel(conditions, context, "conditions", "level filters are not supported for spawner target conditions", warn); StripStates(conditions, context, "conditions", "state filters are only valid for character-drop conditions", warn); StripFactions(conditions, context, "conditions", "faction filters are only valid for character-drop conditions", warn); if (!allowCreatureSpawnerRuntimeOverlapKeys) { StripTimeOfDay(conditions, context, "conditions", "creatureSpawner uses creatureSpawner.timeOfDay for runtime time-of-day gating", warn); StripInsidePlayerBase(conditions, context, "conditions", "creatureSpawner uses allowInsidePlayerBase for runtime player-base gating", warn); StripRequiredGlobalKeys(conditions, context, "conditions", "creatureSpawner uses requiredGlobalKey for runtime global-key gating", warn); StripForbiddenGlobalKeys(conditions, context, "conditions", "creatureSpawner uses blockingGlobalKey for runtime global-key blocking", warn); } } } internal static void StripUnsupportedCreatureSpawnerEntryFields(ConditionsDefinition? conditions, string context, Action warn) { if (conditions != null) { StripTimeOfDay(conditions, context, "conditions", "creatureSpawner uses creatureSpawner.timeOfDay for runtime time-of-day gating", warn); StripInsidePlayerBase(conditions, context, "conditions", "creatureSpawner does not support inside-only top-level player-base gating. Use allowInsidePlayerBase for the runtime permission flag instead", warn); StripRequiredGlobalKeys(conditions, context, "conditions", "creatureSpawner uses requiredGlobalKey for runtime global-key gating", warn); StripForbiddenGlobalKeys(conditions, context, "conditions", "creatureSpawner uses blockingGlobalKey for runtime global-key blocking", warn); } } private static void StripLevel(ConditionsDefinition conditions, string context, string conditionPath, string reason, Action warn) { IntRangeDefinition? level = conditions.Level; if ((level != null && level.HasValues()) || conditions.MinLevel.HasValue || conditions.MaxLevel.HasValue) { WarnUnsupported(context, conditionPath, "level", reason, warn); conditions.Level = null; conditions.MinLevel = null; conditions.MaxLevel = null; } } private static void StripLocations(ConditionsDefinition conditions, string context, string conditionPath, string reason, Action warn) { if (conditions.Locations != null && conditions.Locations.Count != 0) { WarnUnsupported(context, conditionPath, "locations", reason, warn); conditions.Locations = null; } } private static void StripTimeOfDay(ConditionsDefinition conditions, string context, string conditionPath, string reason, Action warn) { if (conditions.TimeOfDay != null) { WarnUnsupported(context, conditionPath, "timeOfDay", reason, warn); conditions.TimeOfDay = null; } } private static void StripRequiredGlobalKeys(ConditionsDefinition conditions, string context, string conditionPath, string reason, Action warn) { if (conditions.RequiredGlobalKeys != null && conditions.RequiredGlobalKeys.Count != 0) { WarnUnsupported(context, conditionPath, "requiredGlobalKeys", reason, warn); conditions.RequiredGlobalKeys = null; } } private static void StripForbiddenGlobalKeys(ConditionsDefinition conditions, string context, string conditionPath, string reason, Action warn) { if (conditions.ForbiddenGlobalKeys != null && conditions.ForbiddenGlobalKeys.Count != 0) { WarnUnsupported(context, conditionPath, "forbiddenGlobalKeys", reason, warn); conditions.ForbiddenGlobalKeys = null; } } private static void StripStates(ConditionsDefinition conditions, string context, string conditionPath, string reason, Action warn) { if (conditions.States != null && conditions.States.Count != 0) { WarnUnsupported(context, conditionPath, "states", reason, warn); conditions.States = null; } } private static void StripFactions(ConditionsDefinition conditions, string context, string conditionPath, string reason, Action warn) { if (conditions.Factions != null && conditions.Factions.Count != 0) { WarnUnsupported(context, conditionPath, "factions", reason, warn); conditions.Factions = null; } } private static void StripInsidePlayerBase(ConditionsDefinition conditions, string context, string conditionPath, string reason, Action warn) { if (conditions.InsidePlayerBase.HasValue) { WarnUnsupported(context, conditionPath, "insidePlayerBase", reason, warn); conditions.InsidePlayerBase = null; } } private static void WarnUnsupported(string context, string conditionPath, string fieldName, string reason, Action warn) { warn("Entry '" + context + "' uses " + conditionPath + "." + fieldName + ", but " + reason + ". The key was ignored."); } } internal sealed class ConditionsDefinition { [YamlMember(Order = 1)] public IntRangeDefinition? Level { get; set; } [YamlMember(Order = 2)] public FloatRangeDefinition? Altitude { get; set; } [YamlIgnore] public int? MinLevel { get; set; } [YamlIgnore] public int? MaxLevel { get; set; } [YamlIgnore] public float? MinAltitude { get; set; } [YamlIgnore] public float? MaxAltitude { get; set; } [YamlMember(Order = 3)] public FloatRangeDefinition? DistanceFromCenter { get; set; } [YamlIgnore] public float? MinDistanceFromCenter { get; set; } [YamlIgnore] public float? MaxDistanceFromCenter { get; set; } [YamlMember(Order = 4)] public List? Biomes { get; set; } [YamlIgnore] public Biome? ResolvedBiomeMask { get; set; } [YamlMember(Order = 5)] public List? Locations { get; set; } [YamlMember(Order = 6)] public TimeOfDayDefinition? TimeOfDay { get; set; } [YamlMember(Order = 7)] public List? RequiredEnvironments { get; set; } [YamlMember(Order = 8)] public List? RequiredGlobalKeys { get; set; } [YamlMember(Order = 9)] public List? ForbiddenGlobalKeys { get; set; } [YamlMember(Order = 10)] public List? States { get; set; } [YamlMember(Order = 11)] public List? Factions { get; set; } [YamlMember(Order = 12)] public bool? InForest { get; set; } [YamlMember(Order = 13)] public bool? InDungeon { get; set; } [YamlMember(Order = 14)] public bool? InsidePlayerBase { get; set; } } internal static class DropConditionEvaluator { private enum CachedLocationResolutionMode { Stable, Spatial } private sealed class CachedLocationResolution { public int Epoch { get; set; } public Vector2i Zone { get; set; } public Vector3 Position { get; set; } public string LocationName { get; set; } = ""; public bool HasLocationName { get; set; } public CachedLocationResolutionMode Mode { get; set; } } private static readonly Dictionary CachedLocationResolutionsByObjectId = new Dictionary(); private const int MaxCachedLocationResolutions = 4096; private const float SpatialResolutionReuseDistanceSqr = 1f; private static int _locationResolutionEpoch; private static int _lastLocationResolutionZoneSystemId; private static int _lastLocationResolutionLocationInstanceCount = -1; internal static bool HasConditions(ConditionsDefinition? conditions) { if (!HasStaticConditions(conditions)) { return HasDynamicConditions(conditions); } return true; } internal static bool HasDynamicConditions(ConditionsDefinition? conditions) { if (conditions != null) { if (!HasAnyValues(conditions.RequiredGlobalKeys) && !HasAnyValues(conditions.ForbiddenGlobalKeys) && conditions.TimeOfDay == null && !HasAnyValues(conditions.RequiredEnvironments)) { return conditions.InsidePlayerBase.HasValue; } return true; } return false; } internal static bool HasStaticConditions(ConditionsDefinition? conditions) { if (conditions != null) { if (!conditions.ResolvedBiomeMask.HasValue && !HasAnyValues(conditions.Biomes) && !HasAnyValues(conditions.Locations) && !conditions.InDungeon.HasValue && !conditions.InForest.HasValue) { FloatRangeDefinition? distanceFromCenter = conditions.DistanceFromCenter; if ((distanceFromCenter == null || !distanceFromCenter.HasValues()) && !conditions.MinDistanceFromCenter.HasValue && !conditions.MaxDistanceFromCenter.HasValue) { FloatRangeDefinition? altitude = conditions.Altitude; if ((altitude == null || !altitude.HasValues()) && !conditions.MinAltitude.HasValue) { return conditions.MaxAltitude.HasValue; } } } return true; } return false; } internal static bool HasCharacterConditions(ConditionsDefinition? conditions) { if (!HasConditions(conditions)) { if (conditions != null) { IntRangeDefinition? level = conditions.Level; if ((level == null || !level.HasValues()) && !conditions.MinLevel.HasValue && !conditions.MaxLevel.HasValue && !HasAnyValues(conditions.States)) { return HasAnyValues(conditions.Factions); } return true; } return false; } return true; } internal static bool AreSatisfied(GameObject gameObject, ConditionsDefinition? conditions) { return AreSatisfied(gameObject, conditions, null); } internal static bool AreSatisfied(GameObject gameObject, ConditionsDefinition? conditions, string? resolvedLocationName) { //IL_0012: 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_0031: 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_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) //IL_0148: Unknown result type (might be due to invalid IL or missing references) //IL_020c: Unknown result type (might be due to invalid IL or missing references) //IL_024f: Unknown result type (might be due to invalid IL or missing references) //IL_0255: Unknown result type (might be due to invalid IL or missing references) //IL_025b: Unknown result type (might be due to invalid IL or missing references) //IL_0260: Unknown result type (might be due to invalid IL or missing references) //IL_0236: Unknown result type (might be due to invalid IL or missing references) //IL_02f4: Unknown result type (might be due to invalid IL or missing references) //IL_030e: Unknown result type (might be due to invalid IL or missing references) //IL_0330: Unknown result type (might be due to invalid IL or missing references) if (!HasConditions(conditions)) { return true; } Vector3 position = gameObject.transform.position; if (HasBiomeCondition(conditions)) { WorldGenerator instance = WorldGenerator.instance; if (!MatchesBiomeCondition((instance != null) ? instance.GetBiome(position) : Heightmap.FindBiome(position), conditions)) { return false; } } if (HasAnyValues(conditions.RequiredGlobalKeys)) { if ((Object)(object)ZoneSystem.instance == (Object)null) { return false; } foreach (string requiredGlobalKey in conditions.RequiredGlobalKeys) { string text = (requiredGlobalKey ?? "").Trim(); if (text.Length != 0 && !ZoneSystem.instance.GetGlobalKey(text)) { return false; } } } if (HasAnyValues(conditions.ForbiddenGlobalKeys) && (Object)(object)ZoneSystem.instance != (Object)null) { foreach (string forbiddenGlobalKey in conditions.ForbiddenGlobalKeys) { string text2 = (forbiddenGlobalKey ?? "").Trim(); if (text2.Length != 0 && ZoneSystem.instance.GetGlobalKey(text2)) { return false; } } } if (HasAnyValues(conditions.Locations)) { string text3 = ResolveLocationName(gameObject, position, resolvedLocationName); if (string.IsNullOrWhiteSpace(text3)) { return false; } string resolvedLocation = text3; if (!conditions.Locations.Any((string name) => MatchesName(resolvedLocation, name))) { return false; } } if (conditions.TimeOfDay != null && !MatchesTimeOfDay(conditions.TimeOfDay)) { return false; } if (HasAnyValues(conditions.RequiredEnvironments)) { EnvMan instance2 = EnvMan.instance; string text4 = ((instance2 == null) ? null : instance2.GetCurrentEnvironment()?.m_name); if (string.IsNullOrWhiteSpace(text4)) { return false; } string resolvedEnvironment = text4; if (!conditions.RequiredEnvironments.Any((string name) => MatchesName(resolvedEnvironment, name))) { return false; } } if (conditions.InDungeon.HasValue && Character.InInterior(position) != conditions.InDungeon.Value) { return false; } if (conditions.InForest.HasValue && WorldGenerator.InForest(position) != conditions.InForest.Value) { return false; } Vector2 val = new Vector2(position.x, position.z); float magnitude = ((Vector2)(ref val)).magnitude; float? min = RangeFormatting.GetMin(conditions.DistanceFromCenter, conditions.MinDistanceFromCenter); float? max = RangeFormatting.GetMax(conditions.DistanceFromCenter, conditions.MinDistanceFromCenter, conditions.MaxDistanceFromCenter); if (min.HasValue && magnitude < min.Value) { return false; } if (max.HasValue && magnitude > max.Value) { return false; } float? min2 = RangeFormatting.GetMin(conditions.Altitude, conditions.MinAltitude); float? max2 = RangeFormatting.GetMax(conditions.Altitude, conditions.MinAltitude, conditions.MaxAltitude); if (min2.HasValue && position.y < min2.Value) { return false; } if (max2.HasValue && position.y > max2.Value) { return false; } if (conditions.InsidePlayerBase.HasValue && (Object)(object)EffectArea.IsPointInsideArea(position, (Type)4, 0f) != (Object)null != conditions.InsidePlayerBase.Value) { return false; } return true; } internal static bool AreStaticConditionsSatisfied(GameObject gameObject, ConditionsDefinition? conditions, string? resolvedLocationName = null) { //IL_0012: 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_0056: 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_0032: 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_002a: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00e3: Unknown result type (might be due to invalid IL or missing references) //IL_00e9: Unknown result type (might be due to invalid IL or missing references) //IL_00ef: Unknown result type (might be due to invalid IL or missing references) //IL_00f4: Unknown result type (might be due to invalid IL or missing references) //IL_00ca: Unknown result type (might be due to invalid IL or missing references) //IL_0188: Unknown result type (might be due to invalid IL or missing references) //IL_01a2: Unknown result type (might be due to invalid IL or missing references) if (!HasStaticConditions(conditions)) { return true; } Vector3 position = gameObject.transform.position; if (HasBiomeCondition(conditions)) { WorldGenerator instance = WorldGenerator.instance; if (!MatchesBiomeCondition((instance != null) ? instance.GetBiome(position) : Heightmap.FindBiome(position), conditions)) { return false; } } if (HasAnyValues(conditions.Locations)) { string text = ResolveLocationName(gameObject, position, resolvedLocationName); if (string.IsNullOrWhiteSpace(text)) { return false; } string resolvedLocation = text; if (!conditions.Locations.Any((string name) => MatchesName(resolvedLocation, name))) { return false; } } if (conditions.InDungeon.HasValue && Character.InInterior(position) != conditions.InDungeon.Value) { return false; } if (conditions.InForest.HasValue && WorldGenerator.InForest(position) != conditions.InForest.Value) { return false; } Vector2 val = new Vector2(position.x, position.z); float magnitude = ((Vector2)(ref val)).magnitude; float? min = RangeFormatting.GetMin(conditions.DistanceFromCenter, conditions.MinDistanceFromCenter); float? max = RangeFormatting.GetMax(conditions.DistanceFromCenter, conditions.MinDistanceFromCenter, conditions.MaxDistanceFromCenter); if (min.HasValue && magnitude < min.Value) { return false; } if (max.HasValue && magnitude > max.Value) { return false; } float? min2 = RangeFormatting.GetMin(conditions.Altitude, conditions.MinAltitude); float? max2 = RangeFormatting.GetMax(conditions.Altitude, conditions.MinAltitude, conditions.MaxAltitude); if (min2.HasValue && position.y < min2.Value) { return false; } if (max2.HasValue && position.y > max2.Value) { return false; } return true; } internal static string? GetResolvedLocationNameForConditions(GameObject gameObject) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)gameObject == (Object)null) { return null; } return ResolveLocationName(gameObject, gameObject.transform.position, null); } internal static bool AreDynamicConditionsSatisfied(ConditionsDefinition? conditions, int timeOfDayPhaseMarker, string? environmentName, bool isInsidePlayerBase, Func getGlobalKeyState) { if (!HasDynamicConditions(conditions)) { return true; } if (HasAnyValues(conditions.RequiredGlobalKeys)) { foreach (string requiredGlobalKey in conditions.RequiredGlobalKeys) { string text = (requiredGlobalKey ?? "").Trim(); if (text.Length != 0 && !getGlobalKeyState(text)) { return false; } } } if (HasAnyValues(conditions.ForbiddenGlobalKeys)) { foreach (string forbiddenGlobalKey in conditions.ForbiddenGlobalKeys) { string text2 = (forbiddenGlobalKey ?? "").Trim(); if (text2.Length != 0 && getGlobalKeyState(text2)) { return false; } } } if (conditions.TimeOfDay != null && !MatchesTimeOfDay(conditions.TimeOfDay, timeOfDayPhaseMarker)) { return false; } if (HasAnyValues(conditions.RequiredEnvironments)) { string resolvedEnvironment = (environmentName ?? "").Trim(); if (resolvedEnvironment.Length == 0 || !conditions.RequiredEnvironments.Any((string name) => MatchesName(resolvedEnvironment, name))) { return false; } } if (conditions.InsidePlayerBase.HasValue && isInsidePlayerBase != conditions.InsidePlayerBase.Value) { return false; } return true; } internal static bool AreSatisfied(Character character, ConditionsDefinition? conditions) { //IL_00c2: 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) if (!HasCharacterConditions(conditions)) { return true; } if ((Object)(object)character == (Object)null) { return false; } if (!AreSatisfied(((Component)character).gameObject, conditions)) { return false; } int? min = RangeFormatting.GetMin(conditions.Level, conditions.MinLevel); int? max = RangeFormatting.GetMax(conditions.Level, conditions.MinLevel, conditions.MaxLevel); if (min.HasValue && character.GetLevel() < min.Value) { return false; } if (max.HasValue && character.GetLevel() > max.Value) { return false; } if (HasAnyValues(conditions.Factions)) { Faction currentFaction = character.GetFaction(); if (!conditions.Factions.Any((string name) => MatchesFaction(currentFaction, name))) { return false; } } if (HasAnyValues(conditions.States) && !conditions.States.Any((string name) => MatchesCreatureState(character, name))) { return false; } return true; } private static bool HasAnyValues(List? values) { return values?.Any((string value) => !string.IsNullOrWhiteSpace(value)) ?? false; } private static bool HasBiomeCondition(ConditionsDefinition? conditions) { if (conditions != null) { if (!conditions.ResolvedBiomeMask.HasValue) { return HasAnyValues(conditions.Biomes); } return true; } return false; } private static bool MatchesBiomeCondition(Biome currentBiome, ConditionsDefinition? conditions) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0021: 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) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Invalid comparison between Unknown and I4 if (conditions != null && conditions.ResolvedBiomeMask.HasValue) { return (currentBiome & conditions.ResolvedBiomeMask.Value) > 0; } if (conditions == null) { return false; } return conditions.Biomes?.Any((string name) => MatchesBiome(currentBiome, name)) == true; } private static bool MatchesBiome(Biome currentBiome, string configuredBiome) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) return BiomeResolutionSupport.MatchesBiome(currentBiome, configuredBiome); } private static bool MatchesName(string currentValue, string configuredValue) { string text = (configuredValue ?? "").Trim(); if (text.Length > 0) { return string.Equals(currentValue, text, StringComparison.OrdinalIgnoreCase); } return false; } private static bool MatchesTimeOfDay(TimeOfDayDefinition configuredTimeOfDay) { return TimeOfDayFormatting.MatchesCurrentTime(configuredTimeOfDay); } private static bool MatchesTimeOfDay(TimeOfDayDefinition configuredTimeOfDay, int phaseMarker) { if (!configuredTimeOfDay.HasValues()) { return false; } return configuredTimeOfDay.Values.Any((string value) => MatchesTimeOfDayToken(value, phaseMarker)); } private static bool MatchesTimeOfDayToken(string token, int phaseMarker) { string text = (token ?? "").Trim().ToLowerInvariant(); if (!(text == "day")) { if (text == "night") { return phaseMarker == 0; } return false; } return phaseMarker == 1; } private static bool MatchesFaction(Faction currentFaction, string configuredFaction) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) return FactionIntegration.Matches(currentFaction, configuredFaction); } private static bool MatchesCreatureState(Character character, string configuredState) { string text = (configuredState ?? "").Trim().ToLowerInvariant(); if (text.Length == 0) { return false; } bool flag = character.IsTamed(); MonsterAI component = ((Component)character).GetComponent(); bool flag2 = (Object)(object)component != (Object)null && component.IsEventCreature(); return text switch { "default" => !flag && !flag2, "tamed" => flag, "event" => flag2, _ => false, }; } private static string? ResolveLocationName(GameObject? gameObject, Vector3 position, string? explicitLocationName) { //IL_0058: 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_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_0082: 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_0076: Unknown result type (might be due to invalid IL or missing references) string text = (explicitLocationName ?? "").Trim(); if (text.Length > 0) { return text; } EnsureLocationResolutionCacheState(); if (SpawnerManager.TryGetResolvedLocationNameForConditions(gameObject, out string locationPrefab) && !string.IsNullOrWhiteSpace(locationPrefab)) { CacheResolvedLocationName(gameObject, position, locationPrefab, CachedLocationResolutionMode.Stable); return locationPrefab; } string owningLocationName = GetOwningLocationName(gameObject); if (!string.IsNullOrWhiteSpace(owningLocationName)) { CacheResolvedLocationName(gameObject, position, owningLocationName, CachedLocationResolutionMode.Stable); return owningLocationName; } if (TryGetCachedLocationResolution(gameObject, position, out string locationName)) { return locationName; } string zoneMatchedLocationName = GetZoneMatchedLocationName(position); if (!string.IsNullOrWhiteSpace(zoneMatchedLocationName)) { CacheResolvedLocationName(gameObject, position, zoneMatchedLocationName, CachedLocationResolutionMode.Spatial); return zoneMatchedLocationName; } string locationName2 = GetLocationName(position); CacheResolvedLocationName(gameObject, position, locationName2, CachedLocationResolutionMode.Spatial); return locationName2; } private static string? GetOwningLocationName(GameObject? gameObject) { if ((Object)(object)gameObject == (Object)null) { return null; } Location componentInParent = gameObject.GetComponentInParent(true); if ((Object)(object)componentInParent != (Object)null && TryResolveLocationPrefabName(componentInParent, out string prefabName) && !string.IsNullOrWhiteSpace(prefabName)) { return prefabName; } if (!((Object)(object)componentInParent != (Object)null)) { return null; } return GetLocationName(componentInParent); } private static string? GetLocationName(Vector3 position) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) Location location = Location.GetLocation(position, true); if ((Object)(object)location != (Object)null && TryResolveLocationPrefabName(location, out string prefabName) && !string.IsNullOrWhiteSpace(prefabName)) { return prefabName; } if (!((Object)(object)location != (Object)null)) { return null; } return GetLocationName(location); } private static string? GetZoneMatchedLocationName(Vector3 position) { //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_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) //IL_002c: 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_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_0056: 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_0087: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)ZoneSystem.instance == (Object)null) { return null; } Vector2i zone = ZoneSystem.GetZone(position); if (!ZoneSystem.instance.m_locationInstances.TryGetValue(zone, out var value)) { return null; } float num = Mathf.Max(value.m_location.m_exteriorRadius, value.m_location.m_interiorRadius); if (num <= 0f || Utils.DistanceXZ(value.m_position, position) > num) { return null; } Location zoneLocation = Location.GetZoneLocation(position); if ((Object)(object)zoneLocation != (Object)null && TryResolveLocationPrefabName(zoneLocation, out string prefabName) && !string.IsNullOrWhiteSpace(prefabName)) { return prefabName; } string zoneLocationPrefabName = GetZoneLocationPrefabName(value.m_location); if (zoneLocationPrefabName.Length <= 0) { return null; } return zoneLocationPrefabName; } private static void EnsureLocationResolutionCacheState() { int num = (((Object)(object)ZoneSystem.instance != (Object)null) ? ((Object)ZoneSystem.instance).GetInstanceID() : 0); int num2 = ZoneSystem.instance?.m_locationInstances.Count ?? 0; if (_lastLocationResolutionZoneSystemId != num || _lastLocationResolutionLocationInstanceCount != num2) { _locationResolutionEpoch++; CachedLocationResolutionsByObjectId.Clear(); _lastLocationResolutionZoneSystemId = num; _lastLocationResolutionLocationInstanceCount = num2; } } private static bool TryGetCachedLocationResolution(GameObject? gameObject, Vector3 position, out string? locationName) { //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002a: 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_003e: 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_0053: 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_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_006b: 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) locationName = null; if ((Object)(object)gameObject == (Object)null) { return false; } if (!CachedLocationResolutionsByObjectId.TryGetValue(((Object)gameObject).GetInstanceID(), out CachedLocationResolution value)) { return false; } Vector2i zone = ZoneSystem.GetZone(position); if (value.Epoch != _locationResolutionEpoch || value.Zone != zone) { return false; } if (value.Mode == CachedLocationResolutionMode.Spatial) { Vector3 val = position - value.Position; if (val.x * val.x + val.z * val.z > 1f) { return false; } } locationName = (value.HasLocationName ? value.LocationName : null); return true; } private static void CacheResolvedLocationName(GameObject? gameObject, Vector3 position, string? locationName, CachedLocationResolutionMode mode) { //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)gameObject == (Object)null)) { if (CachedLocationResolutionsByObjectId.Count >= 4096) { CachedLocationResolutionsByObjectId.Clear(); _locationResolutionEpoch++; } CachedLocationResolutionsByObjectId[((Object)gameObject).GetInstanceID()] = new CachedLocationResolution { Epoch = _locationResolutionEpoch, Zone = ZoneSystem.GetZone(position), Position = position, LocationName = (locationName ?? "").Trim(), HasLocationName = !string.IsNullOrWhiteSpace(locationName), Mode = mode }; } } private static string? GetLocationName(Location location) { //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_003c: 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 (TryResolveLocationPrefabName(location, out string prefabName) && !string.IsNullOrWhiteSpace(prefabName)) { return prefabName; } 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)) { string zoneLocationPrefabName = GetZoneLocationPrefabName(value.m_location); if (zoneLocationPrefabName.Length > 0) { return zoneLocationPrefabName; } } } return TrimCloneSuffix(((Object)((Component)location).gameObject).name); } private static bool TryResolveLocationPrefabName(Location? location, out string prefabName) { //IL_0018: Unknown result type (might be due to invalid IL or missing references) prefabName = ""; if ((Object)(object)location == (Object)null) { return false; } if (TryGetZoneLocationPrefabName(((Component)location).transform.position, out prefabName)) { return true; } prefabName = TrimCloneSuffix(((Object)((Component)location).gameObject).name).Trim(); return prefabName.Length > 0; } private static bool TryGetZoneLocationPrefabName(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_0033: 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_0068: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_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) 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; } float num = Mathf.Max(value.m_location.m_exteriorRadius, value.m_location.m_interiorRadius); if (num > 0f && Utils.DistanceXZ(value.m_position, position) > num) { return false; } string zoneLocationPrefabName = GetZoneLocationPrefabName(value.m_location); if (zoneLocationPrefabName.Length == 0) { return false; } prefabName = zoneLocationPrefabName; return true; } private static string GetZoneLocationPrefabName(ZoneLocation? location) { return (location?.m_prefabName ?? location?.m_prefab.Name ?? "").Trim(); } private static string TrimCloneSuffix(string name) { if (!name.EndsWith("(Clone)", StringComparison.Ordinal)) { return name; } return name.Substring(0, name.Length - "(Clone)".Length); } } internal sealed class InvalidEntryDiagnostics { internal readonly struct SuppressionScope : IDisposable { private readonly InvalidEntryDiagnostics? _owner; public SuppressionScope(InvalidEntryDiagnostics owner) { _owner = owner; _owner._suppressionDepth++; } public void Dispose() { if (_owner != null && _owner._suppressionDepth > 0) { _owner._suppressionDepth--; } } } private readonly HashSet _warnings; private int _suppressionDepth; internal InvalidEntryDiagnostics() { _warnings = new HashSet(StringComparer.OrdinalIgnoreCase); } internal void Clear() { _warnings.Clear(); } internal bool Warn(string message, bool requireSourceOfTruth = false, ISet? capturedWarnings = null) { if ((requireSourceOfTruth && !DropNSpawnPlugin.IsSourceOfTruth) || _suppressionDepth > 0 || ShouldSuppressServerSourcedWarning(message)) { return false; } if (capturedWarnings != null) { capturedWarnings.Add(message); return true; } if (!_warnings.Add(message)) { return false; } DropNSpawnPlugin.DropNSpawnLogger.LogWarning((object)message); return true; } internal SuppressionScope BeginSuppressionForSyncedClientBuild(string sourceName) { if (DropNSpawnPlugin.IsSourceOfTruth || !IsServerSyncSource(sourceName)) { return default(SuppressionScope); } return new SuppressionScope(this); } private static bool IsServerSyncSource(string sourceName) { return sourceName.StartsWith("ServerSync:", StringComparison.Ordinal); } private static bool ShouldSuppressServerSourcedWarning(string message) { if (!DropNSpawnPlugin.IsSourceOfTruth) { return message.IndexOf("ServerSync:", StringComparison.Ordinal) >= 0; } return false; } } internal static class DropNSpawnConsoleCommands { [CompilerGenerated] private static class <>O { public static ConsoleEvent <0>__WriteFullScaffoldFiles; public static ConsoleOptionsFetcher <1>__GetScopedDomainTabOptions; public static ConsoleEvent <2>__WriteReferenceFiles; public static ConsoleEvent <3>__InspectRuntimeTarget; public static ConsoleOptionsFetcher <4>__GetInspectTabOptions; } private const string WriteFullCommandName = "dns:full"; private const string WriteReferenceCommandName = "dns:reference"; private const string InspectCommandName = "dns:inspect"; private static readonly List ScopedDomainTabOptions = new List { "object", "character", "spawner", "spawnsystem", "event", "events", "all" }; private static readonly List InspectTabOptions = new List { "spawner" }; 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 //IL_00c4: 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_00cf: Expected O, but got Unknown //IL_00f2: 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_00e9: Unknown result type (might be due to invalid IL or missing references) //IL_00ef: Expected O, but got Unknown if (!_registered) { _registered = true; object obj = <>O.<0>__WriteFullScaffoldFiles; if (obj == null) { ConsoleEvent val = WriteFullScaffoldFiles; <>O.<0>__WriteFullScaffoldFiles = val; obj = (object)val; } object obj2 = <>O.<1>__GetScopedDomainTabOptions; if (obj2 == null) { ConsoleOptionsFetcher val2 = GetScopedDomainTabOptions; <>O.<1>__GetScopedDomainTabOptions = val2; obj2 = (object)val2; } new ConsoleCommand("dns:full", "Write non-loaded full scaffold YAML files for object/character/spawner/spawnsystem entries with explicit defaults. Event full scaffold is folded into the event reference.", (ConsoleEvent)obj, false, false, false, false, false, (ConsoleOptionsFetcher)obj2, false, false, false); object obj3 = <>O.<2>__WriteReferenceFiles; if (obj3 == null) { ConsoleEvent val3 = WriteReferenceFiles; <>O.<2>__WriteReferenceFiles = val3; obj3 = (object)val3; } object obj4 = <>O.<1>__GetScopedDomainTabOptions; if (obj4 == null) { ConsoleOptionsFetcher val4 = GetScopedDomainTabOptions; <>O.<1>__GetScopedDomainTabOptions = val4; obj4 = (object)val4; } new ConsoleCommand("dns:reference", "Write current generated reference YAML files for object/character/spawner/spawnsystem/event.", (ConsoleEvent)obj3, false, false, false, false, false, (ConsoleOptionsFetcher)obj4, false, false, false); object obj5 = <>O.<3>__InspectRuntimeTarget; if (obj5 == null) { ConsoleEvent val5 = InspectRuntimeTarget; <>O.<3>__InspectRuntimeTarget = val5; obj5 = (object)val5; } object obj6 = <>O.<4>__GetInspectTabOptions; if (obj6 == null) { ConsoleOptionsFetcher val6 = GetInspectTabOptions; <>O.<4>__GetInspectTabOptions = val6; obj6 = (object)val6; } new ConsoleCommand("dns:inspect", "Inspect the current hovered/aimed runtime target. Currently supports: spawner.", (ConsoleEvent)obj5, false, false, false, false, false, (ConsoleOptionsFetcher)obj6, false, false, false); } } private static List GetScopedDomainTabOptions() { return ScopedDomainTabOptions; } private static List GetInspectTabOptions() { return InspectTabOptions; } private static void WriteFullScaffoldFiles(ConsoleEventArgs args) { if (!TryParseScope(args, "dns:full", out var includeObject, out var includeCharacter, out var includeSpawner, out var includeSpawnSystem, out var includeEvent)) { return; } if (includeObject) { if (ObjectDropManager.TryWriteFullScaffoldConfigurationFile(out string path, out string error)) { Terminal context = args.Context; if (context != null) { context.AddString("Wrote object full scaffold to " + path); } } else { Terminal context2 = args.Context; if (context2 != null) { context2.AddString(error); } } } if (includeCharacter) { if (CharacterDropManager.TryWriteFullScaffoldConfigurationFile(out string path2, out string error2)) { Terminal context3 = args.Context; if (context3 != null) { context3.AddString("Wrote character full scaffold to " + path2); } } else { Terminal context4 = args.Context; if (context4 != null) { context4.AddString(error2); } } } if (includeSpawner) { if (SpawnerManager.TryWriteFullScaffoldConfigurationFile(out string path3, out string error3)) { Terminal context5 = args.Context; if (context5 != null) { context5.AddString("Wrote spawner full scaffold to " + path3); } } else { Terminal context6 = args.Context; if (context6 != null) { context6.AddString(error3); } } } if (includeSpawnSystem) { if (SpawnSystemManager.TryWriteFullScaffoldConfigurationFile(out string path4, out string error4)) { Terminal context7 = args.Context; if (context7 != null) { context7.AddString("Wrote spawnsystem full scaffold to " + path4); } } else { Terminal context8 = args.Context; if (context8 != null) { context8.AddString(error4); } } } if (!includeEvent) { return; } if (EventManager.TryWriteFullScaffoldConfigurationFile(out string path5, out string error5)) { Terminal context9 = args.Context; if (context9 != null) { context9.AddString("Wrote event full scaffold to " + path5); } } else { Terminal context10 = args.Context; if (context10 != null) { context10.AddString(error5); } } } private static void WriteReferenceFiles(ConsoleEventArgs args) { if (!TryParseScope(args, "dns:reference", out var includeObject, out var includeCharacter, out var includeSpawner, out var includeSpawnSystem, out var includeEvent)) { return; } if (includeObject) { ObjectDropManager.RefreshReferenceConfigurationFile(); Terminal context = args.Context; if (context != null) { context.AddString("Updated object reference configuration."); } } if (includeCharacter) { CharacterDropManager.RefreshReferenceConfigurationFile(); Terminal context2 = args.Context; if (context2 != null) { context2.AddString("Updated character reference configuration."); } } if (includeSpawner) { SpawnerManager.RefreshReferenceConfigurationFile(); Terminal context3 = args.Context; if (context3 != null) { context3.AddString("Updated spawner reference configuration."); } } if (includeSpawnSystem) { if (SpawnSystemManager.TryWriteReferenceConfigurationFile(out string path, out string error)) { Terminal context4 = args.Context; if (context4 != null) { context4.AddString("Wrote spawnsystem reference to " + path); } } else { Terminal context5 = args.Context; if (context5 != null) { context5.AddString(error); } } } if (!includeEvent) { return; } if (EventManager.TryWriteReferenceConfigurationFile(out string path2, out string error2)) { Terminal context6 = args.Context; if (context6 != null) { context6.AddString("Wrote event reference to " + path2); } } else { Terminal context7 = args.Context; if (context7 != null) { context7.AddString(error2); } } } private static void InspectRuntimeTarget(ConsoleEventArgs args) { if (((args.Length >= 2) ? (args[1] ?? "").Trim().ToLowerInvariant() : "") == "spawner") { if (SpawnerManager.TryInspectCurrentTarget(out string[] lines, out string error)) { string[] array = lines; foreach (string text in array) { Terminal context = args.Context; if (context != null) { context.AddString(text); } } } else { Terminal context2 = args.Context; if (context2 != null) { context2.AddString(error); } } } else { Terminal context3 = args.Context; if (context3 != null) { context3.AddString("Syntax: dns:inspect spawner"); } } } private static bool TryParseScope(ConsoleEventArgs args, string commandName, out bool includeObject, out bool includeCharacter, out bool includeSpawner, out bool includeSpawnSystem, out bool includeEvent) { string text = ((args.Length >= 2) ? (args[1] ?? "").Trim().ToLowerInvariant() : "all"); if (text.Length == 0) { text = "all"; } switch (text) { case "all": includeObject = true; includeCharacter = true; includeSpawner = true; includeSpawnSystem = true; includeEvent = true; return true; case "object": includeObject = true; includeCharacter = false; includeSpawner = false; includeSpawnSystem = false; includeEvent = false; return true; case "character": includeObject = false; includeCharacter = true; includeSpawner = false; includeSpawnSystem = false; includeEvent = false; return true; case "spawner": includeObject = false; includeCharacter = false; includeSpawner = true; includeSpawnSystem = false; includeEvent = false; return true; case "spawnsystem": includeObject = false; includeCharacter = false; includeSpawner = false; includeSpawnSystem = true; includeEvent = false; return true; case "events": case "event": includeObject = false; includeCharacter = false; includeSpawner = false; includeSpawnSystem = false; includeEvent = true; return true; default: { includeObject = false; includeCharacter = false; includeSpawner = false; includeSpawnSystem = false; includeEvent = false; Terminal context = args.Context; if (context != null) { context.AddString("Syntax: " + commandName + " [object|character|spawner|spawnsystem|event|all]"); } return false; } } } } internal static class ExpandWorldFieldOverrideSupport { private static readonly int HashDamage = StringExtensionMethods.GetStableHashCode("damage"); private static readonly HashSet KnownFloatHashes = new HashSet { ZDOVars.s_randomSkillFactor, HashDamage, ZDOVars.s_health, ZDOVars.s_maxHealth, ZDOVars.s_noise }; private static readonly HashSet KnownIntHashes = new HashSet { ZDOVars.s_level, ZDOVars.s_seed, ZDOVars.s_lovePoints }; private static readonly HashSet KnownLongHashes = new HashSet { ZDOVars.s_spawnTime, ZDOVars.s_worldTimeHash, ZDOVars.s_pregnant }; private static readonly HashSet KnownBoolHashes = new HashSet { StringExtensionMethods.GetStableHashCode("bosscount"), ZDOVars.s_isBlockingHash, ZDOVars.s_tamed, ZDOVars.s_aggravated, ZDOVars.s_alert, ZDOVars.s_shownAlertMessage, ZDOVars.s_huntPlayer, ZDOVars.s_patrol, ZDOVars.s_despawnInDay, ZDOVars.s_eventCreature, ZDOVars.s_sleeping, ZDOVars.s_haveSaddleHash }; private static readonly HashSet KnownVectorHashes = new HashSet { ZDOVars.s_bodyVelocity, ZDOVars.s_spawnPoint, ZDOVars.s_patrolPoint }; private static readonly HashSet KnownStringHashes = new HashSet { ZDOVars.s_tamedName, ZDOVars.s_tamedNameAuthor }; internal static void AddFieldOverrides(DataEntry customData, GameObject prefab, Dictionary configuredFields) { Dictionary dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); Dictionary> dictionary2 = new Dictionary>(StringComparer.OrdinalIgnoreCase); foreach (KeyValuePair configuredField in configuredFields) { object obj; string text3; (obj, text3) = (KeyValuePair)(ref configuredField); if (obj == null) { obj = ""; } string text4 = ((string)obj).Trim(); string value = text3 ?? ""; if (text4.Length == 0) { continue; } int stableHashCode = StringExtensionMethods.GetStableHashCode(text4); if (TryInsertKnownOverride(customData, stableHashCode, value)) { continue; } string[] array = text4.Split(new char[1] { '.' }, 2); if (array.Length == 2) { if (!TryInsertTypedOverride(customData, array[0], array[1], value)) { if (!dictionary2.TryGetValue(array[0], out var value2)) { value2 = new Dictionary(StringComparer.OrdinalIgnoreCase); dictionary2[array[0]] = value2; } value2[array[1]] = value; value2["m_" + array[1]] = value; } } else { dictionary[text4] = value; dictionary["m_" + text4] = value; } } prefab.GetComponentsInChildren(ZNetView.m_tempComponents); foreach (MonoBehaviour tempComponent in ZNetView.m_tempComponents) { Type type = ((object)tempComponent).GetType(); string name = type.Name; FieldInfo[] fields = type.GetFields(BindingFlags.Instance | BindingFlags.Public); foreach (FieldInfo fieldInfo in fields) { if (dictionary2.TryGetValue(name, out var value3) && value3.TryGetValue(fieldInfo.Name, out var value4)) { InsertReflectedField(customData, (Component)(object)tempComponent, fieldInfo, value4); } if (dictionary.TryGetValue(fieldInfo.Name, out var value5)) { InsertReflectedField(customData, (Component)(object)tempComponent, fieldInfo, value5); } } } ZNetView.m_tempComponents.Clear(); } private static bool TryInsertKnownOverride(DataEntry customData, int hash, string value) { if (KnownFloatHashes.Contains(hash)) { DataEntry val = customData; if (val.Floats == null) { val.Floats = new Dictionary(); } if (hash == HashDamage) { hash = ZDOVars.s_randomSkillFactor; } customData.Floats[hash] = DataValue.Float(value); return true; } if (KnownIntHashes.Contains(hash)) { DataEntry val = customData; if (val.Ints == null) { val.Ints = new Dictionary(); } customData.Ints[hash] = DataValue.Int(value); return true; } if (KnownLongHashes.Contains(hash)) { DataEntry val = customData; if (val.Longs == null) { val.Longs = new Dictionary(); } customData.Longs[hash] = DataValue.Long(value); return true; } if (KnownBoolHashes.Contains(hash)) { DataEntry val = customData; if (val.Bools == null) { val.Bools = new Dictionary(); } customData.Bools[hash] = DataValue.Bool(value); return true; } if (KnownVectorHashes.Contains(hash)) { DataEntry val = customData; if (val.Vecs == null) { val.Vecs = new Dictionary(); } customData.Vecs[hash] = DataValue.Vector3(value); return true; } if (KnownStringHashes.Contains(hash)) { DataEntry val = customData; if (val.Strings == null) { val.Strings = new Dictionary(); } customData.Strings[hash] = DataValue.String(value); return true; } return false; } private static bool TryInsertTypedOverride(DataEntry customData, string rawPrefix, string rawKey, string value) { string text = (rawKey ?? "").Trim(); if (text.Length == 0) { return false; } int stableHashCode = StringExtensionMethods.GetStableHashCode(text); switch ((rawPrefix ?? "").Trim().ToLowerInvariant()) { case "int": { DataEntry val = customData; if (val.Ints == null) { val.Ints = new Dictionary(); } customData.Ints[stableHashCode] = DataValue.Int(value); return true; } case "float": { DataEntry val = customData; if (val.Floats == null) { val.Floats = new Dictionary(); } customData.Floats[stableHashCode] = DataValue.Float(value); return true; } case "bool": { DataEntry val = customData; if (val.Bools == null) { val.Bools = new Dictionary(); } customData.Bools[stableHashCode] = DataValue.Bool(value); return true; } case "long": { DataEntry val = customData; if (val.Longs == null) { val.Longs = new Dictionary(); } customData.Longs[stableHashCode] = DataValue.Long(value); return true; } case "vec": { DataEntry val = customData; if (val.Vecs == null) { val.Vecs = new Dictionary(); } customData.Vecs[stableHashCode] = DataValue.Vector3(value); return true; } case "quat": { DataEntry val = customData; if (val.Quats == null) { val.Quats = new Dictionary(); } customData.Quats[stableHashCode] = DataValue.Quaternion(value); return true; } case "string": { DataEntry val = customData; if (val.Strings == null) { val.Strings = new Dictionary(); } customData.Strings[stableHashCode] = DataValue.String(value); return true; } default: return false; } } private static void InsertReflectedField(DataEntry customData, Component component, FieldInfo field, string value) { int stableHashCode = StringExtensionMethods.GetStableHashCode(((object)component).GetType().Name + "." + field.Name); DataEntry val = customData; if (val.Ints == null) { val.Ints = new Dictionary(); } customData.Ints[StringExtensionMethods.GetStableHashCode("HasFields")] = DataValue.Simple(1); customData.Ints[StringExtensionMethods.GetStableHashCode("HasFields" + ((object)component).GetType().Name)] = DataValue.Simple(1); if (field.FieldType == typeof(int)) { customData.Ints[stableHashCode] = DataValue.Int(value); } else if (field.FieldType == typeof(float)) { val = customData; if (val.Floats == null) { val.Floats = new Dictionary(); } customData.Floats[stableHashCode] = DataValue.Float(value); } else if (field.FieldType == typeof(bool)) { val = customData; if (val.Bools == null) { val.Bools = new Dictionary(); } customData.Bools[stableHashCode] = DataValue.Bool(value); } else if (field.FieldType == typeof(long)) { val = customData; if (val.Longs == null) { val.Longs = new Dictionary(); } customData.Longs[stableHashCode] = DataValue.Long(value); } else if (field.FieldType == typeof(Vector3)) { val = customData; if (val.Vecs == null) { val.Vecs = new Dictionary(); } customData.Vecs[stableHashCode] = DataValue.Vector3(value); } else if (field.FieldType == typeof(Quaternion)) { val = customData; if (val.Quats == null) { val.Quats = new Dictionary(); } customData.Quats[stableHashCode] = DataValue.Quaternion(value); } else { val = customData; if (val.Strings == null) { val.Strings = new Dictionary(); } customData.Strings[stableHashCode] = DataValue.String(value); } } } internal sealed class ExpandWorldSpawnDataPayload { internal DataEntry? Data { get; } internal List? Objects { get; } internal bool HasObjects { get { List objects = Objects; if (objects != null) { return objects.Count > 0; } return false; } } internal ExpandWorldSpawnDataPayload(DataEntry? data, List? objects) { Data = data; Objects = objects; } } internal static class ExpandWorldSpawnDataSupport { internal static ExpandWorldSpawnDataPayload? BuildPayload(GameObject? prefab, string? dataName, Dictionary? fields, List? objects, string context) { DataEntry val = BuildCustomData(prefab, dataName, fields, context); List list = BuildCustomObjects(objects, context); if (val == null && (list == null || list.Count == 0)) { return null; } return new ExpandWorldSpawnDataPayload(val, list); } internal static void InitializeSpawn(GameObject? prefab, Vector3 spawnPoint, ExpandWorldSpawnDataPayload? payload) { //IL_0019: 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 (!((Object)(object)prefab == (Object)null) && payload?.Data != null) { DataHelper.Init(prefab, spawnPoint, Quaternion.identity, (Vector3?)null, payload.Data); } } internal static void SpawnObjects(Vector3 spawnPoint, ExpandWorldSpawnDataPayload? payload) { //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) if (payload?.Objects == null || payload.Objects.Count == 0) { return; } foreach (BlueprintObject @object in payload.Objects) { if (!(@object.Chance < 1f) || !(Random.value > @object.Chance)) { Spawn.BPO(@object, spawnPoint, Quaternion.identity, Vector3.one, (Func)ObjectDataIdentity, (Func)ObjectPrefabIdentity, (List)null); } } } private static DataEntry? BuildCustomData(GameObject? prefab, string? dataName, Dictionary? fields, string context) { //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Expected O, but got Unknown DataEntry val = null; if (!string.IsNullOrWhiteSpace(dataName)) { val = DataHelper.Get(dataName.Trim(), "DropNSpawn_spawn:" + context); } Dictionary dictionary = NormalizeConfiguredFields(fields); if (dictionary == null) { return val; } if ((Object)(object)prefab == (Object)null) { DropNSpawnPlugin.DropNSpawnLogger.LogWarning((object)("Entry '" + context + "' configured fields but no spawn prefab was available. The fields block was ignored.")); return val; } DataEntry val2 = new DataEntry(); ExpandWorldFieldOverrideSupport.AddFieldOverrides(val2, prefab, dictionary); return DataHelper.Merge(val, val2); } private static Dictionary? NormalizeConfiguredFields(Dictionary? values) { if (values == null) { return null; } Dictionary dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); foreach (KeyValuePair value in values) { object obj; string text3; (obj, text3) = (KeyValuePair)(ref value); if (obj == null) { obj = ""; } string text4 = ((string)obj).Trim(); if (text4.Length != 0) { dictionary[text4] = (text3 ?? "").Trim(); } } if (dictionary.Count != 0) { return dictionary; } return null; } private static List? BuildCustomObjects(List? configuredObjects, string context) { if (configuredObjects == null || configuredObjects.Count == 0) { return null; } List list = new List(); foreach (string configuredObject in configuredObjects) { if (TryParseObjectDefinition(configuredObject, context, out BlueprintObject obj)) { list.Add(obj); } } if (list.Count != 0) { return list; } return null; } private static bool TryParseObjectDefinition(string? rawDefinition, string context, out BlueprintObject? obj) { //IL_00f8: Unknown result type (might be due to invalid IL or missing references) //IL_00fd: 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_010c: Unknown result type (might be due to invalid IL or missing references) //IL_0112: Expected O, but got Unknown obj = null; if (string.IsNullOrWhiteSpace(rawDefinition)) { return false; } string[] array = (from part in rawDefinition.Split(',') select part.Trim()).ToArray(); if (array.Length == 0 || array[0].Length == 0) { DropNSpawnPlugin.DropNSpawnLogger.LogWarning((object)("Entry '" + context + "' contains an invalid objects entry '" + rawDefinition + "'. Expected format: Prefab,posX,posZ,posY,chance,data.")); return false; } if (!TryParseObjectFloat(array, 1, 0f, context, rawDefinition, out var value) || !TryParseObjectFloat(array, 2, 0f, context, rawDefinition, out var value2) || !TryParseObjectFloat(array, 3, 0f, context, rawDefinition, out var value3) || !TryParseObjectFloat(array, 4, 1f, context, rawDefinition, out var value4)) { return false; } DataEntry val = DataHelper.Get((array.Length > 5) ? array[5] : "", "DropNSpawn_spawn:" + context); obj = new BlueprintObject(array[0], new Vector3(value, value3, value2), Quaternion.identity, Vector3.one, val, value4, false); return true; } private static bool TryParseObjectFloat(string[] split, int index, float defaultValue, string context, string rawDefinition, out float value) { value = defaultValue; if (index >= split.Length || split[index].Length == 0) { return true; } if (float.TryParse(split[index], NumberStyles.Any, CultureInfo.InvariantCulture, out value)) { return true; } DropNSpawnPlugin.DropNSpawnLogger.LogWarning((object)("Entry '" + context + "' contains an invalid objects entry '" + rawDefinition + "'. '" + split[index] + "' is not a valid number.")); return false; } private static DataEntry? ObjectDataIdentity(DataEntry? data, string prefab) { return data; } private static string ObjectPrefabIdentity(string prefab) { return prefab; } } internal sealed class EventDefinition { [YamlMember(Alias = "event", Order = 0)] public string? Event { get; set; } [YamlMember(Order = 1)] public List? Settings { get; set; } [YamlMember(Order = 2)] public List? Standalone { get; set; } [YamlMember(Order = 3)] public float? SpawnerDelay { get; set; } [YamlMember(Order = 4)] public EventConditionsDefinition? Conditions { get; set; } [YamlMember(Order = 5)] public List? Messages { get; set; } [YamlMember(Order = 6)] public string? ForceEnvironment { get; set; } [YamlMember(Order = 7)] public string? ForceMusic { get; set; } [YamlMember(Order = 8)] public List? StartCommands { get; set; } [YamlMember(Order = 9)] public List? EndCommands { get; set; } [YamlMember(Order = 10)] public List? Spawns { get; set; } } internal sealed class EventConditionsDefinition { [YamlMember(Order = 0)] public List? Biomes { get; set; } [YamlMember(Order = 1)] public List? PlayerBase { get; set; } [YamlMember(Order = 2)] public List? RequiredEnvironments { get; set; } [YamlMember(Order = 3)] public List? Players { get; set; } [YamlMember(Order = 4)] public List? RequiredGlobalKeys { get; set; } [YamlMember(Order = 5)] public List? ForbiddenGlobalKeys { get; set; } [YamlMember(Order = 6)] public List? RequiredKnownItems { get; set; } [YamlMember(Order = 7)] public List? ForbiddenKnownItems { get; set; } [YamlMember(Order = 8)] public List? RequiredPlayerKeysAny { get; set; } [YamlMember(Order = 9)] public List? RequiredPlayerKeysAll { get; set; } [YamlMember(Order = 10)] public List? ForbiddenPlayerKeys { get; set; } } internal sealed class EventSpawnDefinition { [YamlMember(Order = 0)] public string? Prefab { get; set; } [YamlMember(Order = 1)] public bool? Enabled { get; set; } [YamlMember(Order = 2)] public SpawnSystemSpawnDefinition? SpawnSystem { get; set; } } internal static class EventManager { private sealed class EventRuntimeMetadata { internal PlayerBaseCondition? PlayerBase { get; set; } internal List? RequiredEnvironments { get; set; } internal IntRangeDefinition? PlayerLimit { get; set; } internal float PlayerDistance { get; set; } = 100f; internal List? StartCommands { get; set; } internal List? EndCommands { get; set; } internal bool HasConditionValues() { if (PlayerBase == null && (RequiredEnvironments?.Count ?? 0) <= 0) { return PlayerLimit?.HasValues() ?? false; } return true; } internal bool HasValues() { if (!HasConditionValues() && (StartCommands?.Count ?? 0) <= 0) { return (EndCommands?.Count ?? 0) > 0; } return true; } } private sealed class EventReferenceOutputEntry { internal EventDefinition Definition { get; } internal string OwnerName { get; } internal EventReferenceOutputEntry(EventDefinition definition, string ownerName) { Definition = definition; OwnerName = (string.IsNullOrWhiteSpace(ownerName) ? "Unknown / Untracked" : ownerName.Trim()); } } private sealed class KnownVanillaEventVariantGroup { internal string Name { get; } internal RandomEvent Preferred { get; } internal List Variants { get; } internal KnownVanillaEventVariantGroup(string name, RandomEvent preferred, List variants) { Name = name; Preferred = preferred; Variants = variants; } } private sealed class PlayerBaseCondition { internal bool AllowNear { get; set; } internal bool AllowAway { get; set; } internal bool IsNearOnly { get { if (AllowNear) { return !AllowAway; } return false; } } } private const string DomainName = "events"; private const string ReferenceAutoUpdateStateKey = "events"; internal static readonly DomainModuleDefinition Module = new DomainModuleDefinition(new DomainModuleOptions { DomainKey = "events", ReloadDomain = DropNSpawnPlugin.ReloadDomain.Event, ManifestSettingKey = "events_manifest", ManifestPriority = 95, ShouldReloadForPath = ShouldReloadForPath, Reload = ReloadConfiguration, InitializeRuntime = Initialize, OnGameDataReady = NotifyGameDataReady, HandleExpandWorldDataReady = HandleExpandWorldDataReady, DtoVersion = 2, TransportProfile = DomainTransportProfile.MediumConfig, DisplayName = "events", CacheDirectoryName = "events", ClientRequestPriority = 95, KeySelector = (EventDefinition entry) => entry.Event ?? "", ApplyPayloadAction = ApplySyncedPayload, BeforeClientManifestChanged = MarkSyncedPayloadPending, OnClientAuthorityCutover = EnterPendingSyncedPayloadState }); private static readonly object Sync = new object(); private static readonly IDeserializer Deserializer = new DeserializerBuilder().WithNamingConvention(CamelCaseNamingConvention.Instance).Build(); private static bool _initialized; private static string _configurationSignature = ""; private static bool _synchronizedPayloadReady; private static List ActiveDefinitions = new List(); private static List? BaselineEvents; private static float? BaselineEventChance; private static float? BaselineEventIntervalMinutes; private static readonly List AppliedSpawnData = new List(); private static readonly Dictionary EventMetadata = new Dictionary(); private static readonly List MultipleActiveEvents = new List(); private static MethodInfo? ExpandWorldCommandRunMethod; private static bool ExpandWorldCommandRunMethodLookedUp; private static bool MissingCommandManagerWarningLogged; private static readonly DomainConfigurationRuntime> ConfigurationRuntime = new DomainConfigurationRuntime>(new DomainLoadHooks>(ParseLocalConfigurationDocuments, BuildSyncedConfigurationState, CommitConfigurationState, RejectLocalConfigurationPayload, (List state) => state.Count, null, null, OnSourceOfTruthPayloadUnchanged, PublishSyncedConfiguration), new DomainSyncHooks>(delegate(out List configuration, out string payloadToken) { return ConfigurationDomainHost.TryGetSyncedEntries(Descriptor, out configuration, out payloadToken); }, (string payloadToken) => ConfigurationDomainHost.ShouldSkipSyncedPayload(LoadState, payloadToken, _synchronizedPayloadReady), BuildSyncedConfigurationState, CommitConfigurationState, (List state) => state.Count, "ServerSync:DropNSpawnEvents", MarkSyncedPayloadPending)); internal static DomainDescriptor Descriptor => Module.DescriptorTyped; internal static DomainTransportMetadata TransportMetadata => Module.TransportMetadataTyped; private static string DomainPrefix => PluginSettingsFacade.GetYamlDomainFilePrefix("events"); private static string PrimaryOverrideConfigurationPathYml => Path.Combine(DropNSpawnPlugin.YamlConfigDirectoryPath, DomainPrefix + ".yml"); private static string PrimaryOverrideConfigurationPathYaml => Path.Combine(DropNSpawnPlugin.YamlConfigDirectoryPath, DomainPrefix + ".yaml"); private static string ReferenceConfigurationPath => Path.Combine(DropNSpawnPlugin.YamlConfigDirectoryPath, DomainPrefix + ".reference.yml"); private static DomainLoadState LoadState => ConfigurationRuntime.LoadState; internal static void Initialize() { lock (Sync) { if (!_initialized) { Directory.CreateDirectory(DropNSpawnPlugin.YamlConfigDirectoryPath); EnsureDefaultOverrideFile(); DomainReloadOutcome domainReloadOutcome = LoadConfiguration(); if (DropNSpawnPlugin.IsSourceOfTruth || domainReloadOutcome == DomainReloadOutcome.Loaded) { ApplyLoadedConfigurationLocked(DropNSpawnPlugin.IsSourceOfTruth ? "events override YAML" : "synced events"); } _initialized = true; } } } internal static void Dispose() { EventGlobalConfig.Dispose(); lock (Sync) { ClearAppliedPayloadsLocked(); EventMetadata.Clear(); MultipleActiveEvents.Clear(); if ((Object)(object)RandEventSystem.instance != (Object)null && BaselineEvents != null) { RandEventSystem.instance.m_events = CloneEvents(BaselineEvents); } if ((Object)(object)RandEventSystem.instance != (Object)null && BaselineEventChance.HasValue) { RandEventSystem.instance.m_eventChance = BaselineEventChance.Value; } if ((Object)(object)RandEventSystem.instance != (Object)null && BaselineEventIntervalMinutes.HasValue) { RandEventSystem.instance.m_eventIntervalMin = BaselineEventIntervalMinutes.Value; } if ((Object)(object)RandEventSystem.instance != (Object)null) { RandEventSystem.SetRandomEventsNeedsRefresh(); } BaselineEvents = null; BaselineEventChance = null; BaselineEventIntervalMinutes = null; ActiveDefinitions = new List(); _initialized = false; _synchronizedPayloadReady = false; _configurationSignature = ""; ConfigurationRuntime.ResetLoadState(); } } internal static void NotifyGameDataReady(string source) { lock (Sync) { CaptureBaselineIfNeeded(); ApplyGlobalEventSettingsLocked(); ApplyActiveDefinitionsLocked(source); EnsureReferenceConfigurationFileUpToDateLocked(); } } internal static void ApplyGlobalEventSettings() { lock (Sync) { ApplyGlobalEventSettingsLocked(); } } internal static void ReapplyEventDefinitions(string source) { lock (Sync) { CaptureBaselineIfNeeded(); ApplyActiveDefinitionsLocked(source); EnsureReferenceConfigurationFileUpToDateLocked(); } } internal static void OnRandEventSystemDestroyed() { lock (Sync) { BaselineEvents = null; BaselineEventChance = null; BaselineEventIntervalMinutes = null; ClearAppliedPayloadsLocked(); EventMetadata.Clear(); MultipleActiveEvents.Clear(); } } internal static void CopySpawnPayloads(RandomEvent source, RandomEvent clone) { if (source == null || clone == null) { return; } if (EventMetadata.TryGetValue(source, out EventRuntimeMetadata value)) { EventMetadata[clone] = CloneMetadata(value); } if (source.m_spawn != null && clone.m_spawn != null) { int num = Math.Min(source.m_spawn.Count, clone.m_spawn.Count); for (int i = 0; i < num; i++) { SpawnSystemCustomDataSupport.CopyPreparedPayload(source.m_spawn[i], clone.m_spawn[i]); } } } internal static void ReloadConfiguration() { lock (Sync) { DomainReloadOutcome domainReloadOutcome = LoadConfiguration(); if (DropNSpawnPlugin.IsSourceOfTruth || domainReloadOutcome == DomainReloadOutcome.Loaded) { ApplyLoadedConfigurationLocked(DropNSpawnPlugin.IsSourceOfTruth ? "events override YAML" : "synced events"); } } } internal static bool TryWriteReferenceConfigurationFile(out string path, out string error) { path = ReferenceConfigurationPath; error = ""; if (!TryGetReferenceEvents(out List events)) { error = "Event baseline is not ready yet. Join a world and wait for ZoneSystem.Start before writing event reference YAML."; return false; } try { string text = BuildReferenceYaml(events); GeneratedArtifactWriter.WriteText(path, text, "Wrote event reference configuration at " + path + "."); ReferenceArtifactLifecycle.RecordUpdate("events", ReferenceConfigurationPath, ReferenceRefreshSupport.ComputeStableHash(text)); return true; } catch (Exception ex) { error = "Failed to write event reference configuration: " + ex.Message; return false; } } internal static bool TryWriteFullScaffoldConfigurationFile(out string path, out string error) { path = ReferenceConfigurationPath; error = "Event full scaffold is not generated. DNS_events.reference.yml contains the full event-level schema with compact spawn entries."; return false; } private static bool TryGetReferenceEvents(out List events) { if (BaselineEvents == null) { events = new List(); return false; } events = CloneEvents(BaselineEvents); return true; } private static void EnsureReferenceConfigurationFileUpToDateLocked() { if (TryGetReferenceEvents(out List events)) { string text; try { text = BuildReferenceYaml(events); } catch (Exception ex) { DropNSpawnPlugin.DropNSpawnLogger.LogWarning((object)("Failed to build event reference configuration: " + ex.Message)); return; } string sourceSignature = ReferenceRefreshSupport.ComputeStableHash(text); if (ReferenceArtifactLifecycle.TryPlanUpdate("events", ReferenceConfigurationPath, sourceSignature, out var updateKind)) { GeneratedArtifactWriter.WriteText(ReferenceConfigurationPath, text, ReferenceArtifactLifecycle.FormatAction(updateKind) + " event reference configuration at " + ReferenceConfigurationPath + "."); ReferenceArtifactLifecycle.RecordUpdate("events", ReferenceConfigurationPath, sourceSignature); } } } internal static void MarkSyncedPayloadPending() { lock (Sync) { ConfigurationRuntime.MarkSyncedPayloadPending(DropNSpawnPlugin.IsSourceOfTruth, delegate { _synchronizedPayloadReady = false; }); } } internal static void EnterPendingSyncedPayloadState() { lock (Sync) { ConfigurationRuntime.EnterPendingSyncedPayloadState(DropNSpawnPlugin.IsSourceOfTruth, null, delegate { _synchronizedPayloadReady = false; _configurationSignature = ""; }); } } internal static void ApplySyncedPayload() { lock (Sync) { ConfigurationRuntime.ApplySyncedPayload(delegate { ApplyLoadedConfigurationLocked("synced events"); }); } } internal static bool HandleExpandWorldDataReady() { lock (Sync) { if (!DropNSpawnPlugin.IsSourceOfTruth) { return false; } CaptureBaselineIfNeeded(); ApplyActiveDefinitionsLocked("ExpandWorldData ready"); EnsureReferenceConfigurationFileUpToDateLocked(); return true; } } private static DomainReloadOutcome LoadConfiguration() { if (DropNSpawnPlugin.IsSourceOfTruth) { EnsureDefaultOverrideFile(); return ConfigurationRuntime.ReloadSourceOfTruth(EnumerateOverrideConfigurationPaths().ToList()); } return ConfigurationRuntime.ReloadSynced(); } private static void ApplyLoadedConfigurationLocked(string source) { CaptureBaselineIfNeeded(); ApplyActiveDefinitionsLocked(source); EnsureReferenceConfigurationFileUpToDateLocked(); } private static List BuildSyncedConfigurationState(List definitions, string sourceName) { List list = NetworkPayloadSyncSupport.CloneEntries(Descriptor, definitions); NormalizeDefinitions(list); return list; } private static void CommitConfigurationState(List configuration, string payloadToken) { ActiveDefinitions = configuration; _configurationSignature = NetworkPayloadSyncSupport.ComputeEventConfigurationSignature(ActiveDefinitions); LoadState.LastLoadedPayload = payloadToken; LoadState.LastRejectedPayload = ""; LoadState.PendingStrictPayload = ""; LoadState.LastRejectedValidationKey = ""; _synchronizedPayloadReady = true; } private static void PublishSyncedConfiguration() { ConfigurationDomainHost.PublishSyncedPayload(DropNSpawnPlugin.IsSourceOfTruth, Descriptor, ActiveDefinitions, _configurationSignature); } private static void OnSourceOfTruthPayloadUnchanged() { if (!NetworkPayloadSyncSupport.IsPayloadCurrent(Descriptor, _configurationSignature)) { PublishSyncedConfiguration(); } } internal static bool ShouldReloadForPath(string? path) { if (PluginSettingsFacade.IsEligibleOverrideConfigurationPath(path)) { return IsOverrideConfigurationFileName(Path.GetFileName(path ?? "")); } return false; } private static void EnsureDefaultOverrideFile() { if (!DomainConfigurationFileSupport.HasAnyOverrideConfigurationFile("events", PrimaryOverrideConfigurationPathYml, PrimaryOverrideConfigurationPathYaml)) { GeneratedArtifactWriter.WriteText(PrimaryOverrideConfigurationPathYml, BuildDefaultOverrideYaml(), "Created event override configuration at " + PrimaryOverrideConfigurationPathYml + ".", logOnlyWhenChanged: true); } } private static LocalLoadResult ParseLocalConfigurationDocuments(List documents) { List list = new List(); List list2 = new List(); int num = 0; foreach (ConfigurationLoadSupport.LocalYamlDocument document in documents) { if (document.ReadError != null) { list2.Add("Failed to load event override YAML '" + document.Path + "': " + document.ReadError); continue; } if (!TryParseYaml(document.Yaml ?? "", Path.GetFileName(document.Path), out List definitions, out string error)) { list2.Add(error); continue; } list.AddRange(definitions); num++; } return new LocalLoadResult { Entries = list, Errors = list2, ParsedEntryCount = list.Count, LoadedFileCount = num }; } private static void RejectLocalConfigurationPayload(string payload, IEnumerable errors) { ConfigurationDomainHost.RejectLocalConfigurationPayload(LoadState, payload, errors, "events"); } private static IEnumerable EnumerateOverrideConfigurationPaths() { return DomainConfigurationFileSupport.EnumerateOverrideConfigurationPaths("events", PrimaryOverrideConfigurationPathYml, PrimaryOverrideConfigurationPathYaml); } private static bool IsOverrideConfigurationFileName(string fileName) { return DomainConfigurationFileSupport.IsOverrideConfigurationFileName("events", fileName); } private static bool TryParseYaml(string yaml, string sourceName, out List definitions, out string error) { definitions = new List(); error = ""; if (string.IsNullOrWhiteSpace(yaml)) { return true; } try { definitions = Deserializer.Deserialize>(yaml) ?? new List(); for (int i = 0; i < definitions.Count; i++) { EventDefinition eventDefinition = definitions[i]; if (eventDefinition == null) { error = $"Failed to parse event YAML '{sourceName}': entry {i + 1} is null."; definitions = new List(); return false; } List spawns = eventDefinition.Spawns; if (spawns != null && spawns.Any((EventSpawnDefinition spawn) => spawn == null)) { error = $"Failed to parse event YAML '{sourceName}': entry {i + 1} contains a null spawn."; definitions = new List(); return false; } } NormalizeDefinitions(definitions); return true; } catch (Exception ex) { error = "Failed to parse event YAML '" + sourceName + "': " + ex.Message; definitions = new List(); return false; } } private static void NormalizeDefinitions(List definitions) { foreach (EventDefinition definition in definitions) { definition.Event = NormalizeOptionalString(definition.Event); definition.Settings = NormalizePositionalStringList(definition.Settings); NormalizeConditions(definition.Conditions); definition.Messages = NormalizeOptionalStringList(definition.Messages); definition.ForceEnvironment = NormalizeOptionalStringPreserveEmpty(definition.ForceEnvironment); definition.ForceMusic = NormalizeOptionalStringPreserveEmpty(definition.ForceMusic); definition.StartCommands = NormalizeOptionalStringList(definition.StartCommands); definition.EndCommands = NormalizeOptionalStringList(definition.EndCommands); NormalizeSpawns(definition.Spawns); } } private static void NormalizeConditions(EventConditionsDefinition? conditions) { if (conditions != null) { conditions.Biomes = NormalizeOptionalStringList(conditions.Biomes); conditions.PlayerBase = NormalizeOptionalStringList(conditions.PlayerBase); conditions.RequiredEnvironments = NormalizeOptionalStringList(conditions.RequiredEnvironments)?.Select((string value) => value.ToLowerInvariant()).ToList(); conditions.Players = NormalizeOptionalStringList(conditions.Players); conditions.RequiredGlobalKeys = NormalizeOptionalStringList(conditions.RequiredGlobalKeys); conditions.ForbiddenGlobalKeys = NormalizeOptionalStringList(conditions.ForbiddenGlobalKeys); conditions.RequiredPlayerKeysAny = NormalizeOptionalStringList(conditions.RequiredPlayerKeysAny); conditions.RequiredPlayerKeysAll = NormalizeOptionalStringList(conditions.RequiredPlayerKeysAll); conditions.ForbiddenPlayerKeys = NormalizeOptionalStringList(conditions.ForbiddenPlayerKeys); conditions.RequiredKnownItems = NormalizeOptionalStringList(conditions.RequiredKnownItems); conditions.ForbiddenKnownItems = NormalizeOptionalStringList(conditions.ForbiddenKnownItems); } } private static void NormalizeSpawns(List? spawns) { if (spawns == null) { return; } foreach (EventSpawnDefinition spawn in spawns) { spawn.Prefab = NormalizeOptionalString(spawn.Prefab); } } private static void CaptureBaselineIfNeeded() { if (BaselineEvents == null && !((Object)(object)RandEventSystem.instance == (Object)null)) { BaselineEvents = CloneEvents(RandEventSystem.instance.m_events); } } private static void ApplyGlobalEventSettingsLocked() { if (!((Object)(object)RandEventSystem.instance == (Object)null)) { float valueOrDefault = BaselineEventChance.GetValueOrDefault(); if (!BaselineEventChance.HasValue) { valueOrDefault = RandEventSystem.instance.m_eventChance; BaselineEventChance = valueOrDefault; } valueOrDefault = BaselineEventIntervalMinutes.GetValueOrDefault(); if (!BaselineEventIntervalMinutes.HasValue) { valueOrDefault = RandEventSystem.instance.m_eventIntervalMin; BaselineEventIntervalMinutes = valueOrDefault; } RandEventSystem.instance.m_eventChance = PluginSettingsFacade.GetRandomEventChance(); RandEventSystem.instance.m_eventIntervalMin = PluginSettingsFacade.GetRandomEventIntervalMinutes(); if (!PluginSettingsFacade.IsMultipleEventsEnabled()) { StopAllMultipleEventsLocked(RandEventSystem.instance, sendUpdate: true); } } } internal static bool TryRunMultipleEventsFixedUpdate(RandEventSystem eventSystem) { if ((Object)(object)eventSystem == (Object)null || !PluginSettingsFacade.IsMultipleEventsEnabled() || !IsEventServer()) { return false; } float dt = Time.fixedDeltaTime; eventSystem.UpdateForcedEvents(dt); eventSystem.UpdateRandomEvent(dt); RandomEvent forcedEvent = eventSystem.m_forcedEvent; if (forcedEvent != null) { forcedEvent.Update(true, true, true, dt); } StopMultipleEventsLocked(MultipleActiveEvents.Where(delegate(RandomEvent activeEvent) { bool flag = eventSystem.IsAnyPlayerInEventArea(activeEvent); return activeEvent.Update(true, true, flag, dt); }).ToList(), callOnStop: true); SelectLocalMultipleEvent(eventSystem); return true; } internal static bool TryHandleMultipleSetRandomEvent(RandEventSystem eventSystem, RandomEvent ev, Vector3 pos) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0060: 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) if ((Object)(object)eventSystem == (Object)null || !PluginSettingsFacade.IsMultipleEventsEnabled() || !IsEventServer()) { return false; } if (ev == null) { StopAllMultipleEventsLocked(eventSystem, sendUpdate: true); return true; } float minimumDistance = PluginSettingsFacade.GetMinimumDistanceBetweenEvents(); if (MultipleActiveEvents.Any((RandomEvent activeEvent) => Utils.DistanceXZ(activeEvent.m_pos, pos) < minimumDistance)) { return true; } RandomEvent val = ev.Clone(); val.m_pos = pos; val.OnStart(); MultipleActiveEvents.Add(val); eventSystem.m_randomEvent = val; eventSystem.SendCurrentRandomEvent(); return true; } internal static bool TrySendMultipleCurrentRandomEvent(RandEventSystem eventSystem) { //IL_00f7: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)eventSystem == (Object)null || !PluginSettingsFacade.IsMultipleEventsEnabled() || !IsEventServer()) { return false; } if (eventSystem.m_forcedEvent != null) { SendEventToEveryone(eventSystem.m_forcedEvent); return true; } if (MultipleActiveEvents.Count == 0) { return false; } if (MultipleActiveEvents.Count == 1) { SendEventToEveryone(MultipleActiveEvents[0]); return true; } if ((Object)(object)ZNet.instance == (Object)null || ZRoutedRpc.instance == null) { return true; } foreach (ZNetPeer peer in ZNet.instance.GetPeers()) { if (peer.m_rpc != null) { RandomEvent val = MultipleActiveEvents.OrderBy((RandomEvent activeEvent) => Utils.DistanceXZ(activeEvent.m_pos, peer.m_refPos)).First(); ZRoutedRpc.instance.InvokeRoutedRPC(peer.m_uid, "SetEvent", new object[3] { val.m_name, val.m_time, val.m_pos }); } } return true; } internal static bool TryRunCheckPerPlayerRandomUpdate(RandEventSystem eventSystem, float dt) { if ((Object)(object)eventSystem == (Object)null || !PluginSettingsFacade.IsEventCheckPerPlayerEnabled() || !IsEventServer() || Game.m_eventRate == 0f) { return false; } if (RandEventSystem.s_randomEventNeedsRefresh) { RandEventSystem.RefreshPlayerEventData(); } CheckGlobalEventsPerPlayer(eventSystem, dt); CheckStandaloneEventsPerPlayer(eventSystem, dt); eventSystem.m_sendTimer += dt; if (eventSystem.m_sendTimer > 2f) { eventSystem.m_sendTimer = 0f; eventSystem.SendCurrentRandomEvent(); } return true; } private static void SelectLocalMultipleEvent(RandEventSystem eventSystem) { //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_0077: Unknown result type (might be due to invalid IL or missing references) if (eventSystem.m_forcedEvent != null) { eventSystem.SetActiveEvent(eventSystem.m_forcedEvent, false); return; } if ((Object)(object)Player.m_localPlayer == (Object)null) { eventSystem.m_randomEvent = null; eventSystem.SetActiveEvent((RandomEvent)null, false); return; } Vector3 playerPosition = ((Component)Player.m_localPlayer).transform.position; RandomEvent val = (eventSystem.m_randomEvent = MultipleActiveEvents.OrderBy((RandomEvent activeEvent) => Utils.DistanceXZ(activeEvent.m_pos, playerPosition)).FirstOrDefault()); if (val != null && eventSystem.IsInsideRandomEventArea(val, playerPosition)) { eventSystem.SetActiveEvent(val, false); } else { eventSystem.SetActiveEvent((RandomEvent)null, false); } } private static void CheckGlobalEventsPerPlayer(RandEventSystem eventSystem, float dt) { //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_0067: 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) eventSystem.m_eventTimer += dt; if (eventSystem.m_eventTimer <= eventSystem.m_eventIntervalMin * 60f * Game.m_eventRate) { return; } eventSystem.m_eventTimer = 0f; foreach (PlayerEventData s_playerEventData in RandEventSystem.s_playerEventDatas) { if (!(Random.Range(0f, 100f) > eventSystem.m_eventChance / Game.m_eventRate)) { List> possibleRandomEvents = GetPossibleRandomEvents(eventSystem, s_playerEventData); if (possibleRandomEvents.Count != 0) { KeyValuePair keyValuePair = possibleRandomEvents[Random.Range(0, possibleRandomEvents.Count)]; eventSystem.SetRandomEvent(keyValuePair.Key, keyValuePair.Value); } } } } private static void CheckStandaloneEventsPerPlayer(RandEventSystem eventSystem, float dt) { //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_0095: 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_00f0: Unknown result type (might be due to invalid IL or missing references) List list = null; foreach (RandomEvent @event in eventSystem.m_events) { if (!@event.m_enabled || @event.m_standaloneInterval <= 0f || eventSystem.m_activeEvent == @event) { continue; } @event.m_time += dt; if (@event.m_time <= @event.m_standaloneInterval * Game.m_eventRate) { continue; } if (@event.m_standaloneChance > 0f) { if (list == null) { list = new List(1); } foreach (PlayerEventData s_playerEventData in RandEventSystem.s_playerEventDatas) { list.Clear(); list.Add(s_playerEventData); if (!(Random.Range(0f, 100f) > @event.m_standaloneChance / Game.m_eventRate) && eventSystem.HaveGlobalKeys(@event, list)) { List validEventPoints = eventSystem.GetValidEventPoints(@event, list); if (validEventPoints.Count != 0) { eventSystem.SetRandomEvent(@event, validEventPoints[Random.Range(0, validEventPoints.Count)]); } } } } @event.m_time = 0f; } } private static List> GetPossibleRandomEvents(RandEventSystem eventSystem, PlayerEventData player) { //IL_0012: 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) //IL_0075: Unknown result type (might be due to invalid IL or missing references) eventSystem.m_lastPossibleEvents.Clear(); List list = new List(1) { player }; foreach (RandomEvent @event in eventSystem.m_events) { if (@event.m_enabled && @event.m_random && eventSystem.HaveGlobalKeys(@event, list)) { List validEventPoints = eventSystem.GetValidEventPoints(@event, list); if (validEventPoints.Count != 0) { Vector3 value = validEventPoints[Random.Range(0, validEventPoints.Count)]; eventSystem.m_lastPossibleEvents.Add(new KeyValuePair(@event, value)); } } } return eventSystem.m_lastPossibleEvents; } private static void StopAllMultipleEventsLocked(RandEventSystem? eventSystem, bool sendUpdate) { if (MultipleActiveEvents.Count == 0) { return; } StopMultipleEventsLocked(MultipleActiveEvents.ToList(), callOnStop: true); if ((Object)(object)eventSystem != (Object)null) { eventSystem.m_randomEvent = null; if (sendUpdate) { eventSystem.SendCurrentRandomEvent(); } } } private static void StopMultipleEventsLocked(List activeEvents, bool callOnStop) { if (activeEvents.Count == 0) { return; } foreach (RandomEvent activeEvent in activeEvents) { if (callOnStop) { activeEvent.OnStop(); } EventMetadata.Remove(activeEvent); } MultipleActiveEvents.RemoveAll(activeEvents.Contains); } private static void SendEventToEveryone(RandomEvent randomEvent) { //IL_0037: Unknown result type (might be due to invalid IL or missing references) if (ZRoutedRpc.instance != null) { ZRoutedRpc.instance.InvokeRoutedRPC(ZRoutedRpc.Everybody, "SetEvent", new object[3] { randomEvent.m_name, randomEvent.m_time, randomEvent.m_pos }); } } private static bool IsEventServer() { if ((Object)(object)ZNet.instance != (Object)null) { return ZNet.instance.IsServer(); } return false; } private static void ApplyActiveDefinitionsLocked(string source) { //IL_0151: Unknown result type (might be due to invalid IL or missing references) //IL_0158: Expected O, but got Unknown if ((Object)(object)RandEventSystem.instance == (Object)null || BaselineEvents == null) { return; } ClearAppliedPayloadsLocked(); EventMetadata.Clear(); List list = CloneEvents(BaselineEvents); HashSet hashSet = new HashSet(); if (PluginSettingsFacade.IsEventDomainEnabled()) { List groups = FindKnownVanillaEventVariantGroups(list); HashSet hashSet2 = new HashSet(StringComparer.OrdinalIgnoreCase); Dictionary> dictionary = list.Where((RandomEvent ev) => !string.IsNullOrWhiteSpace(ev.m_name)).GroupBy((RandomEvent ev) => ev.m_name, StringComparer.OrdinalIgnoreCase).ToDictionary, string, List>((IGrouping group) => group.Key, (IGrouping group) => group.ToList(), StringComparer.OrdinalIgnoreCase); for (int num = 0; num < ActiveDefinitions.Count; num++) { EventDefinition eventDefinition = ActiveDefinitions[num]; string text = eventDefinition.Event ?? ""; if (text.Length == 0) { DropNSpawnPlugin.DropNSpawnLogger.LogWarning((object)("Event override entry " + num.ToString(CultureInfo.InvariantCulture) + " is missing 'event'.")); continue; } if (!dictionary.TryGetValue(text, out var value)) { RandomEvent item = new RandomEvent(); value = (dictionary[text] = new List { item }); list.Add(item); } hashSet2.Add(text); for (int num2 = 0; num2 < value.Count; num2++) { RandomEvent val = value[num2]; val.m_name = text; string context = ((value.Count == 1) ? (source + ":" + text) : (source + ":" + text + "[" + (num2 + 1).ToString(CultureInfo.InvariantCulture) + "]")); if (ApplyDefinition(val, eventDefinition, context)) { hashSet.Add(val); } } } CollapseEquivalentKnownVanillaEventVariants(list, groups, hashSet2); } ApplyEventDurationMultiplier(list, hashSet); ApplyDefaultPlayerBaseConditions(list); RandEventSystem.instance.m_events = list; RandEventSystem.SetRandomEventsNeedsRefresh(); } private static void ApplyDefaultPlayerBaseConditions(List events) { PlayerBaseCondition playerBaseCondition = CreateDefaultPlayerBaseCondition(PluginSettingsFacade.GetDefaultEventPlayerBase()); if (playerBaseCondition == null) { return; } foreach (RandomEvent @event in events) { if (@event == null) { continue; } if (EventMetadata.TryGetValue(@event, out EventRuntimeMetadata value)) { if (value.PlayerBase != null) { continue; } } else { value = new EventRuntimeMetadata(); } value.PlayerBase = ClonePlayerBaseCondition(playerBaseCondition); @event.m_nearBaseOnly = playerBaseCondition.IsNearOnly; EventMetadata[@event] = value; } } private static PlayerBaseCondition? CreateDefaultPlayerBaseCondition(EventGlobalConfig.EventPlayerBaseDefault mode) { return mode switch { EventGlobalConfig.EventPlayerBaseDefault.Away => new PlayerBaseCondition { AllowAway = true }, EventGlobalConfig.EventPlayerBaseDefault.Near => new PlayerBaseCondition { AllowNear = true }, EventGlobalConfig.EventPlayerBaseDefault.AwayAndNear => new PlayerBaseCondition { AllowNear = true, AllowAway = true }, _ => null, }; } private static void ApplyEventDurationMultiplier(IEnumerable events, ISet explicitDurationEvents) { float eventDurationMultiplier = PluginSettingsFacade.GetEventDurationMultiplier(); if (eventDurationMultiplier == 1f) { return; } foreach (RandomEvent @event in events) { if (@event != null && !(@event.m_duration <= 0f)) { if (eventDurationMultiplier <= 0f) { @event.m_enabled = false; } else if (!explicitDurationEvents.Contains(@event)) { @event.m_duration *= eventDurationMultiplier; } } } } private static bool ApplyDefinition(RandomEvent target, EventDefinition definition, string context) { if (definition.SpawnerDelay.HasValue) { target.m_spawnerDelay = Math.Max(0f, definition.SpawnerDelay.Value); } bool result = ApplySettings(target, definition.Settings, context); ApplyStandalone(target, definition.Standalone); EventRuntimeMetadata eventRuntimeMetadata = BuildMetadata(definition, context); ApplyConditions(target, definition.Conditions, eventRuntimeMetadata, context); ApplyMessages(target, definition.Messages); ApplyForces(target, definition); if (eventRuntimeMetadata.HasValues()) { EventMetadata[target] = eventRuntimeMetadata; } if (definition.Spawns != null) { target.m_spawn = BuildSpawnList(definition.Spawns, context); } return result; } private static bool ApplySettings(RandomEvent target, List? settings, string context) { if (settings == null || settings.Count == 0) { return false; } bool result = false; if (TryParseBool(settings, 0, target.m_enabled, context, "settings[0]", out var result2)) { target.m_enabled = result2; } if (TryParseBool(settings, 1, target.m_random, context, "settings[1]", out var result3)) { target.m_random = result3; } if (TryParseFloat(settings, 2, target.m_duration, context, "settings[2]", out var result4)) { target.m_duration = Math.Max(0f, result4); result = true; } if (TryParseFloat(settings, 3, target.m_eventRange, context, "settings[3]", out var result5)) { target.m_eventRange = Math.Max(0f, result5); } if (TryParseBool(settings, 4, target.m_pauseIfNoPlayerInArea, context, "settings[4]", out var result6)) { target.m_pauseIfNoPlayerInArea = result6; } return result; } private static void ApplyStandalone(RandomEvent target, List? standalone) { if (standalone != null && standalone.Count != 0) { target.m_standaloneInterval = Math.Max(0f, standalone[0]); if (standalone.Count > 1) { target.m_standaloneChance = standalone[1]; } } } private static void ApplyBiomes(RandomEvent target, List? biomes, string context) { //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) if (biomes != null && TryParseBiomes(biomes, context, out var biomes2)) { target.m_biome = biomes2; } } private static EventRuntimeMetadata BuildMetadata(EventDefinition definition, string context) { EventRuntimeMetadata eventRuntimeMetadata = new EventRuntimeMetadata { StartCommands = definition.StartCommands?.ToList(), EndCommands = definition.EndCommands?.ToList() }; EventConditionsDefinition conditions = definition.Conditions; if (conditions == null) { return eventRuntimeMetadata; } if (TryParsePlayerBaseCondition(conditions.PlayerBase, context, out PlayerBaseCondition condition)) { eventRuntimeMetadata.PlayerBase = condition; } if (conditions.RequiredEnvironments != null) { eventRuntimeMetadata.RequiredEnvironments = conditions.RequiredEnvironments.ToList(); } if (conditions.Players != null && conditions.Players.Count > 0) { if (TryParseIntRange(conditions.Players[0], context, "conditions.players[0]", out IntRangeDefinition range)) { eventRuntimeMetadata.PlayerLimit = range; } if (TryParseFloat(conditions.Players, 1, eventRuntimeMetadata.PlayerDistance, context, "conditions.players[1]", out var result)) { eventRuntimeMetadata.PlayerDistance = Math.Max(0f, result); } } return eventRuntimeMetadata; } private static void ApplyConditions(RandomEvent target, EventConditionsDefinition? conditions, EventRuntimeMetadata metadata, string context) { if (conditions != null) { ApplyBiomes(target, conditions.Biomes, context); if (metadata.PlayerBase != null) { target.m_nearBaseOnly = metadata.PlayerBase.IsNearOnly; } if (conditions.RequiredGlobalKeys != null) { target.m_requiredGlobalKeys = conditions.RequiredGlobalKeys.ToList(); } if (conditions.ForbiddenGlobalKeys != null) { target.m_notRequiredGlobalKeys = conditions.ForbiddenGlobalKeys.ToList(); } if (conditions.RequiredPlayerKeysAny != null) { target.m_altRequiredPlayerKeysAny = conditions.RequiredPlayerKeysAny.ToList(); } if (conditions.RequiredPlayerKeysAll != null) { target.m_altRequiredPlayerKeysAll = conditions.RequiredPlayerKeysAll.ToList(); } if (conditions.ForbiddenPlayerKeys != null) { target.m_altNotRequiredPlayerKeys = conditions.ForbiddenPlayerKeys.ToList(); } if (conditions.RequiredKnownItems != null) { target.m_altRequiredKnownItems = ResolveItemDrops(conditions.RequiredKnownItems, context); } if (conditions.ForbiddenKnownItems != null) { target.m_altRequiredNotKnownItems = ResolveItemDrops(conditions.ForbiddenKnownItems, context); } } } private static void ApplyMessages(RandomEvent target, List? messages) { if (messages != null) { target.m_startMessage = ((messages.Count > 0) ? messages[0] : ""); target.m_endMessage = ((messages.Count > 1) ? messages[1] : ""); } } private static void ApplyForces(RandomEvent target, EventDefinition definition) { if (definition.ForceEnvironment != null) { target.m_forceEnvironment = definition.ForceEnvironment; } if (definition.ForceMusic != null) { target.m_forceMusic = definition.ForceMusic; } } private static List BuildSpawnList(List definitions, string context) { //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Expected O, but got Unknown List list = new List(); for (int i = 0; i < definitions.Count; i++) { CanonicalSpawnSystemEntry entry = ConvertEventSpawnToSpawnSystemEntry(definitions[i]); SpawnSystemManager.NormalizeEntry(entry); SpawnData val = new SpawnData(); string context2 = context + ".spawns[" + i.ToString(CultureInfo.InvariantCulture) + "]"; if (SpawnSystemManager.ApplyEntry(val, entry, context2, applyCustomData: false)) { SpawnSystemCustomDataSupport.PreparedPayload payload = SpawnSystemCustomDataSupport.BuildPreparedPayload(val, entry, context2); SpawnSystemCustomDataSupport.ApplyPreparedPayload(val, payload); AppliedSpawnData.Add(val); list.Add(val); } } return list; } private static void ClearAppliedPayloadsLocked() { foreach (SpawnData appliedSpawnDatum in AppliedSpawnData) { SpawnSystemCustomDataSupport.ApplyPreparedPayload(appliedSpawnDatum, null); } AppliedSpawnData.Clear(); } private static void RemoveAppliedEventState(RandomEvent ev) { EventMetadata.Remove(ev); foreach (SpawnData item in ev.m_spawn ?? new List()) { if (AppliedSpawnData.Remove(item)) { SpawnSystemCustomDataSupport.ApplyPreparedPayload(item, null); } } } private static string BuildReferenceYaml(List events) { List list = events ?? new List(); List knownVanillaVariantGroups = FindKnownVanillaEventVariantGroups(list); PrefabOwnerResolver.OwnerSnapshot ownerSnapshot = PrefabOwnerResolver.GetSnapshot(); List entries = (from ev in list where ev != null where !knownVanillaVariantGroups.Any((KnownVanillaEventVariantGroup @group) => @group.Variants.Any((RandomEvent variant) => variant == ev) && @group.Preferred != ev) select ev).Select(delegate(RandomEvent ev) { EventDefinition definition = ConvertToDefinition(ev, includeSpawnDefaults: false); SuppressReferenceOnlyFields(definition); return new EventReferenceOutputEntry(definition, ResolveEventOwnerName(definition, ownerSnapshot)); }).ToList(); return BuildGeneratedYamlHeader("reference") + BuildEventReferenceDefinitionsYaml(entries); } private static List FindKnownVanillaEventVariantGroups(IEnumerable events) { List list = new List(); foreach (IGrouping item in (events ?? Enumerable.Empty()).Where((RandomEvent ev) => ev != null && !string.IsNullOrWhiteSpace(ev.m_name)).GroupBy((RandomEvent ev) => ev.m_name, StringComparer.OrdinalIgnoreCase)) { if (!TryGetKnownVanillaEventVariantKeys(item.Key, out string preferredKey, out string legacyKey)) { continue; } List list2 = item.ToList(); if (list2.Count == 2) { List list3 = list2.Where((RandomEvent ev) => HasSingleRequiredPlayerKey(ev, preferredKey)).ToList(); List list4 = list2.Where((RandomEvent ev) => HasSingleRequiredPlayerKey(ev, legacyKey)).ToList(); if (list3.Count == 1 && list4.Count == 1) { list.Add(new KnownVanillaEventVariantGroup(item.Key, list3[0], list2)); } } } return list; } private static bool TryGetKnownVanillaEventVariantKeys(string eventName, out string preferredKey, out string legacyKey) { if (string.Equals(eventName, "army_moder", StringComparison.OrdinalIgnoreCase)) { preferredKey = "GP_Bonemass"; legacyKey = "$se_bonemass_name"; return true; } if (string.Equals(eventName, "army_theelder", StringComparison.OrdinalIgnoreCase)) { preferredKey = "GP_Eikthyr"; legacyKey = "GP_TheElder"; return true; } preferredKey = ""; legacyKey = ""; return false; } private static bool HasSingleRequiredPlayerKey(RandomEvent ev, string expectedKey) { List list = ev.m_altRequiredPlayerKeysAny ?? new List(); if (list.Count == 1) { return string.Equals(list[0], expectedKey, StringComparison.OrdinalIgnoreCase); } return false; } private static void CollapseEquivalentKnownVanillaEventVariants(List events, IEnumerable groups, ISet overriddenEventNames) { foreach (KnownVanillaEventVariantGroup group in groups) { if (!overriddenEventNames.Contains(group.Name)) { continue; } string preferredSignature = ComputeComparableEventSignature(group.Preferred); if (group.Variants.Any((RandomEvent ev) => !string.Equals(ComputeComparableEventSignature(ev), preferredSignature, StringComparison.Ordinal))) { continue; } foreach (RandomEvent variant in group.Variants) { if (variant != group.Preferred) { RemoveAppliedEventState(variant); int num = events.FindIndex((RandomEvent candidate) => candidate == variant); if (num >= 0) { events.RemoveAt(num); } } } } } private static string ComputeComparableEventSignature(RandomEvent ev) { EventDefinition eventDefinition = ConvertToDefinition(ev, includeSpawnDefaults: true); return NetworkPayloadSyncSupport.ComputeEventConfigurationSignature(new EventDefinition[1] { eventDefinition }); } private static string BuildEventReferenceDefinitionsYaml(List entries) { if (entries.Count == 0) { return "[]\n"; } List> list = PrefabOutputSections.BuildSections(entries, (EventReferenceOutputEntry entry) => entry.Definition.Event ?? "", (EventReferenceOutputEntry entry) => entry.OwnerName); StringBuilder stringBuilder = new StringBuilder(); bool flag = false; foreach (PrefabOwnerSection item in list) { if (item.Entries.Count == 0) { continue; } if (flag) { stringBuilder.AppendLine(); } PrefabOutputSections.AppendSectionHeaderComment(stringBuilder, item.OwnerName); foreach (EventReferenceOutputEntry entry in item.Entries) { AppendEventDefinition(stringBuilder, entry.Definition); } flag = true; } if (!flag) { return "[]\n"; } return stringBuilder.ToString(); } private static string ResolveEventOwnerName(EventDefinition definition, PrefabOwnerResolver.OwnerSnapshot ownerSnapshot) { List source = (from spawn in definition.Spawns ?? new List() select ownerSnapshot.GetOwnerName(spawn.Prefab) into ownerName where !string.IsNullOrWhiteSpace(ownerName) select ownerName.Trim()).ToList(); string text = (from @group in (from @group in source.Where((string ownerName) => !string.Equals(ownerName, "Unknown / Untracked", StringComparison.OrdinalIgnoreCase) && !string.Equals(ownerName, "Valheim", StringComparison.OrdinalIgnoreCase)).GroupBy((string ownerName) => ownerName, StringComparer.OrdinalIgnoreCase) orderby @group.Count() descending select @group).ThenBy, string>((IGrouping @group) => @group.Key, StringComparer.OrdinalIgnoreCase) select @group.Key).FirstOrDefault(); if (!string.IsNullOrWhiteSpace(text)) { return text; } if (source.Any((string ownerName) => string.Equals(ownerName, "Valheim", StringComparison.OrdinalIgnoreCase))) { return "Valheim"; } return "Unknown / Untracked"; } private static void SuppressReferenceOnlyFields(EventDefinition definition) { definition.Standalone = null; definition.SpawnerDelay = null; definition.StartCommands = null; definition.EndCommands = null; } private static string BuildGeneratedYamlHeader(string kind) { return "# Generated DropNSpawn event " + kind + ".\n# Edit DNS_events.yml instead; generated files may be overwritten.\n\n"; } private static void AppendEventDefinition(StringBuilder builder, EventDefinition definition) { AppendYamlListEntryLine(builder, 0, "event", definition.Event); AppendYamlOptionalRawListLine(builder, 1, "settings", definition.Settings); AppendYamlOptionalFloatListLine(builder, 1, "standalone", definition.Standalone); AppendYamlOptionalFloatLine(builder, 1, "spawnerDelay", definition.SpawnerDelay); AppendEventConditions(builder, definition.Conditions); AppendYamlOptionalInlineListLine(builder, 1, "messages", definition.Messages); AppendYamlStringLine(builder, 1, "forceEnvironment", definition.ForceEnvironment); AppendYamlStringLine(builder, 1, "forceMusic", definition.ForceMusic); AppendYamlOptionalInlineListLine(builder, 1, "startCommands", definition.StartCommands); AppendYamlOptionalInlineListLine(builder, 1, "endCommands", definition.EndCommands); AppendEventSpawns(builder, definition.Spawns); } private static void AppendEventConditions(StringBuilder builder, EventConditionsDefinition? conditions) { if (conditions != null) { AppendYamlLine(builder, 1, "conditions:"); AppendYamlOptionalInlineListLine(builder, 2, "biomes", conditions.Biomes); AppendYamlOptionalRawListLine(builder, 2, "playerBase", conditions.PlayerBase); AppendYamlOptionalInlineListLine(builder, 2, "requiredEnvironments", conditions.RequiredEnvironments); AppendYamlOptionalRawListLine(builder, 2, "players", conditions.Players); AppendYamlOptionalInlineListLine(builder, 2, "requiredGlobalKeys", conditions.RequiredGlobalKeys); AppendYamlOptionalInlineListLine(builder, 2, "forbiddenGlobalKeys", conditions.ForbiddenGlobalKeys); AppendYamlOptionalInlineListLine(builder, 2, "requiredKnownItems", conditions.RequiredKnownItems); AppendYamlOptionalInlineListLine(builder, 2, "forbiddenKnownItems", conditions.ForbiddenKnownItems); AppendYamlOptionalInlineListLine(builder, 2, "requiredPlayerKeysAny", conditions.RequiredPlayerKeysAny); AppendYamlOptionalInlineListLine(builder, 2, "requiredPlayerKeysAll", conditions.RequiredPlayerKeysAll); AppendYamlOptionalInlineListLine(builder, 2, "forbiddenPlayerKeys", conditions.ForbiddenPlayerKeys); } } private static void AppendEventSpawns(StringBuilder builder, List? spawns) { //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Expected O, but got Unknown if (spawns == null || spawns.Count == 0) { return; } AppendYamlLine(builder, 1, "spawns:"); foreach (EventSpawnDefinition spawn in spawns) { AppendYamlListEntryLine(builder, 2, "prefab", spawn.Prefab); AppendYamlOptionalBoolLine(builder, 3, "enabled", spawn.Enabled); SpawnSystemManager.AppendYamlSpawnSystemPayloadBlock(builder, 3, ConvertEventSpawnToSpawnSystemEntry(spawn), new SpawnData(), includeEmptyPlaceholder: false); } } private static CanonicalSpawnSystemEntry ConvertEventSpawnToSpawnSystemEntry(EventSpawnDefinition spawn) { return new CanonicalSpawnSystemEntry { Prefab = spawn.Prefab, Enabled = (spawn.Enabled ?? true), SpawnSystem = spawn.SpawnSystem }; } private static void AppendYamlLine(StringBuilder builder, int indent, string text) { builder.Append(' ', indent * 2); builder.AppendLine(text); } private static void AppendYamlListEntryLine(StringBuilder builder, int indent, string key, string? value) { builder.Append(' ', indent * 2); builder.Append("- ").Append(key).Append(": ") .AppendLine(FormatYamlString(value)); } private static void AppendYamlStringLine(StringBuilder builder, int indent, string key, string? value) { builder.Append(' ', indent * 2); builder.Append(key).Append(": ").AppendLine(FormatYamlString(value)); } private static void AppendYamlOptionalBoolLine(StringBuilder builder, int indent, string key, bool? value) { if (value.HasValue) { AppendYamlLine(builder, indent, key + ": " + FormatYamlBool(value.Value)); } } private static void AppendYamlOptionalFloatLine(StringBuilder builder, int indent, string key, float? value) { if (value.HasValue) { AppendYamlLine(builder, indent, key + ": " + FormatFloat(value.Value)); } } private static void AppendYamlOptionalInlineListLine(StringBuilder builder, int indent, string key, List? values) { if (values != null) { AppendYamlLine(builder, indent, key + ": " + FormatYamlInlineList(values)); } } private static void AppendYamlOptionalRawListLine(StringBuilder builder, int indent, string key, List? values) { if (values != null) { AppendYamlLine(builder, indent, key + ": " + FormatYamlInlineRawList(values)); } } private static void AppendYamlOptionalFloatListLine(StringBuilder builder, int indent, string key, List? values) { if (values != null) { AppendYamlLine(builder, indent, key + ": [" + string.Join(", ", values.Select(FormatFloat)) + "]"); } } private static string FormatYamlInlineList(List? values) { if (values != null && values.Count != 0) { return "[" + string.Join(", ", values.Select(FormatYamlString)) + "]"; } return "[]"; } private static string FormatYamlInlineRawList(List? values) { if (values != null && values.Count != 0) { return "[" + string.Join(", ", values.Select((string value) => (!string.IsNullOrWhiteSpace(value)) ? value.Trim() : "''")) + "]"; } return "[]"; } private static string FormatYamlBool(bool value) { if (!value) { return "false"; } return "true"; } private static string FormatYamlString(string? value) { string text = value ?? ""; if (text.Length == 0) { return "''"; } if (text.All(delegate(char character) { bool flag = char.IsLetterOrDigit(character); if (!flag) { bool flag2; switch (character) { case '$': case '-': case '.': case '/': case '_': flag2 = true; break; default: flag2 = false; break; } flag = flag2; } return flag; }) && !bool.TryParse(text, out var _) && !string.Equals(text, "null", StringComparison.OrdinalIgnoreCase) && !float.TryParse(text, NumberStyles.Float, CultureInfo.InvariantCulture, out var _)) { return text; } return "'" + text.Replace("'", "''") + "'"; } private static EventDefinition ConvertToDefinition(RandomEvent ev, bool includeSpawnDefaults) { List source = ev.m_spawn ?? new List(); EventRuntimeMetadata value; EventRuntimeMetadata eventRuntimeMetadata = (EventMetadata.TryGetValue(ev, out value) ? value : null); EventDefinition eventDefinition = new EventDefinition { Event = ev.m_name, Settings = new List { ev.m_enabled.ToString().ToLowerInvariant(), ev.m_random.ToString().ToLowerInvariant(), FormatFloat(ev.m_duration), FormatFloat(ev.m_eventRange), ev.m_pauseIfNoPlayerInArea.ToString().ToLowerInvariant() }, Standalone = new List { ev.m_standaloneInterval, ev.m_standaloneChance }, SpawnerDelay = ev.m_spawnerDelay, Conditions = ConvertConditions(ev, eventRuntimeMetadata), Messages = new List { ev.m_startMessage ?? "", ev.m_endMessage ?? "" }, ForceEnvironment = (ev.m_forceEnvironment ?? ""), ForceMusic = (ev.m_forceMusic ?? ""), StartCommands = (eventRuntimeMetadata?.StartCommands?.ToList() ?? new List()), EndCommands = (eventRuntimeMetadata?.EndCommands?.ToList() ?? new List()), Spawns = (from spawn in source where spawn != null select ConvertSpawnDefinition(spawn, includeSpawnDefaults)).ToList() }; if (!includeSpawnDefaults && eventDefinition.Spawns.Count == 0) { eventDefinition.Spawns = null; } return eventDefinition; } private static EventConditionsDefinition ConvertConditions(RandomEvent ev, EventRuntimeMetadata? metadata) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) List playerBase = ConvertPlayerBaseCondition(metadata?.PlayerBase, ev.m_nearBaseOnly); List biomes = ConvertBiomes(ev.m_biome); List requiredEnvironments = metadata?.RequiredEnvironments?.ToList() ?? new List(); IntRangeDefinition intRangeDefinition = ((metadata?.PlayerLimit != null) ? CloneRange(metadata.PlayerLimit) : null); List source = ev.m_requiredGlobalKeys ?? new List(); List source2 = ev.m_notRequiredGlobalKeys ?? new List(); List itemDrops = ev.m_altRequiredKnownItems ?? new List(); List source3 = ev.m_altRequiredPlayerKeysAny ?? new List(); List source4 = ev.m_altRequiredPlayerKeysAll ?? new List(); List itemDrops2 = ev.m_altRequiredNotKnownItems ?? new List(); List source5 = ev.m_altNotRequiredPlayerKeys ?? new List(); return new EventConditionsDefinition { Biomes = biomes, PlayerBase = playerBase, RequiredEnvironments = requiredEnvironments, Players = new List { RangeFormatting.FormatShorthand(intRangeDefinition ?? RangeFormatting.From(0, null)), FormatFloat(metadata?.PlayerDistance ?? 100f) }, RequiredGlobalKeys = source.ToList(), ForbiddenGlobalKeys = source2.ToList(), RequiredPlayerKeysAny = source3.ToList(), RequiredPlayerKeysAll = source4.ToList(), ForbiddenPlayerKeys = source5.ToList(), RequiredKnownItems = ConvertItemDrops(itemDrops), ForbiddenKnownItems = ConvertItemDrops(itemDrops2) }; } private static List ConvertPlayerBaseCondition(PlayerBaseCondition? condition, bool nearBaseOnly) { if (condition == null) { if (!nearBaseOnly) { return new List { "near", "away" }; } return new List { "near" }; } if (condition.AllowNear && condition.AllowAway) { return new List { "near", "away" }; } if (condition.AllowNear) { return new List { "near" }; } if (condition.AllowAway) { return new List { "away" }; } return new List { "near", "away" }; } private static EventSpawnDefinition ConvertSpawnDefinition(SpawnData data, bool full) { //IL_0219: Unknown result type (might be due to invalid IL or missing references) //IL_022a: Unknown result type (might be due to invalid IL or missing references) if (!full) { CanonicalSpawnSystemEntry canonicalSpawnSystemEntry = SpawnSystemManager.CreateReferenceEntryForExternalProjection(data); return new EventSpawnDefinition { Prefab = canonicalSpawnSystemEntry.Prefab, Enabled = (canonicalSpawnSystemEntry.Enabled ? ((bool?)null) : new bool?(false)), SpawnSystem = canonicalSpawnSystemEntry.SpawnSystem }; } return new EventSpawnDefinition { Prefab = GetPrefabName(data.m_prefab), Enabled = data.m_enabled, SpawnSystem = new SpawnSystemSpawnDefinition { Name = NormalizeOptionalString(data.m_name), HuntPlayer = data.m_huntPlayer, Level = RangeFormatting.From(data.m_minLevel, data.m_maxLevel), LevelUpMinCenterDistance = data.m_levelUpMinCenterDistance, OverrideLevelUpChance = data.m_overrideLevelupChance, GroundOffset = data.m_groundOffset, GroundOffsetRandom = data.m_groundOffsetRandom, SpawnInterval = data.m_spawnInterval, SpawnChance = data.m_spawnChance, SpawnRadius = RangeFormatting.From(data.m_spawnRadiusMin, data.m_spawnRadiusMax), GroupSize = RangeFormatting.From(data.m_groupSizeMin, data.m_groupSizeMax), GroupRadius = data.m_groupRadius, NoSpawnRadius = data.m_spawnDistance, MaxSpawned = data.m_maxSpawned, Tilt = RangeFormatting.From(data.m_minTilt, data.m_maxTilt), Altitude = RangeFormatting.From(data.m_minAltitude, data.m_maxAltitude), OceanDepth = RangeFormatting.From(data.m_minOceanDepth, data.m_maxOceanDepth), DistanceFromCenter = RangeFormatting.From(data.m_minDistanceFromCenter, data.m_maxDistanceFromCenter), Biomes = ConvertBiomes(data.m_biome), BiomeAreas = ConvertBiomeAreas(data.m_biomeArea), TimeOfDay = TimeOfDayFormatting.FromSpawnFlags(data.m_spawnAtDay, data.m_spawnAtNight), RequiredEnvironments = (from value in data.m_requiredEnvironments ?? new List() select value.Trim() into value where value.Length > 0 select value).ToList(), RequiredGlobalKey = NormalizeOptionalString(data.m_requiredGlobalKey), InLava = ConvertExclusiveZoneToggle(data.m_inLava, data.m_outsideLava), InForest = ConvertExclusiveZoneToggle(data.m_inForest, data.m_outsideForest), InsidePlayerBase = data.m_insidePlayerBase, CanSpawnCloseToPlayer = data.m_canSpawnCloseToPlayer } }; } private static bool? ConvertExclusiveZoneToggle(bool inside, bool outside) { if (inside && !outside) { return true; } if (!inside && outside) { return false; } return null; } internal static bool TryCheckBase(RandomEvent ev, PlayerEventData player, out bool result) { //IL_0017: Unknown result type (might be due to invalid IL or missing references) result = false; if (!TryGetMetadata(ev, out EventRuntimeMetadata metadata) || metadata.PlayerBase == null) { return false; } bool flag = player.baseValue >= 3; result = (flag ? metadata.PlayerBase.AllowNear : metadata.PlayerBase.AllowAway); return true; } internal static bool PassesExtraChecks(RandomEvent ev, Vector3 point, bool currentResult) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) if (!currentResult) { return false; } if (!TryGetMetadata(ev, out EventRuntimeMetadata metadata)) { return true; } if (PassesEnvironmentCheck(point, metadata.RequiredEnvironments)) { return PassesPlayerCountCheck(point, metadata.PlayerLimit, metadata.PlayerDistance); } return false; } internal static void RunStartCommands(RandomEvent ev) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) if (TryGetMetadata(ev, out EventRuntimeMetadata metadata)) { RunCommands(metadata.StartCommands, ev.m_pos); } } internal static void RunEndCommands(RandomEvent ev) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) if (TryGetMetadata(ev, out EventRuntimeMetadata metadata)) { RunCommands(metadata.EndCommands, ev.m_pos); } } private static bool TryGetMetadata(RandomEvent? ev, out EventRuntimeMetadata metadata) { metadata = null; if (ev == null) { return false; } if (EventMetadata.TryGetValue(ev, out metadata)) { return true; } if ((Object)(object)RandEventSystem.instance == (Object)null || string.IsNullOrWhiteSpace(ev.m_name)) { return false; } RandomEvent val = RandEventSystem.instance.GetEvent(ev.m_name); if (val != null) { return EventMetadata.TryGetValue(val, out metadata); } return false; } private static bool PassesEnvironmentCheck(Vector3 point, List? requiredEnvironments) { //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) if (requiredEnvironments == null || requiredEnvironments.Count == 0) { return true; } if (WorldGenerator.instance == null || (Object)(object)EnvMan.instance == (Object)null || (Object)(object)ZNet.instance == (Object)null) { return false; } Biome biome = WorldGenerator.instance.GetBiome(point); List availableEnvironments = EnvMan.instance.GetAvailableEnvironments(biome); if (availableEnvironments == null || availableEnvironments.Count == 0) { return false; } State state = Random.state; Random.InitState((int)(ZNet.instance.GetTimeSeconds() / (double)EnvMan.instance.m_environmentDuration)); EnvSetup val = EnvMan.instance.SelectWeightedEnvironment(availableEnvironments); Random.state = state; if (val != null) { return requiredEnvironments.Contains((val.m_name ?? "").ToLowerInvariant()); } return false; } private static bool PassesPlayerCountCheck(Vector3 point, IntRangeDefinition? limit, float distance) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) if (limit == null || !limit.HasValues()) { return true; } int value = RandEventSystem.s_playerEventDatas.Count((PlayerEventData player) => Utils.DistanceXZ(point, player.position) <= distance); return RangeContains(limit, value, 0, int.MaxValue); } private static void RunCommands(List? commands, Vector3 position) { //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_006b: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) List list = NormalizeOptionalStringList(commands) ?? new List(); if (list.Count == 0) { return; } ZNet instance = ZNet.instance; if (instance != null && !instance.IsServer()) { return; } MethodInfo expandWorldCommandRunMethod = GetExpandWorldCommandRunMethod(); if (expandWorldCommandRunMethod == null) { if (!MissingCommandManagerWarningLogged) { MissingCommandManagerWarningLogged = true; DropNSpawnPlugin.DropNSpawnLogger.LogWarning((object)"Event commands require ExpandWorldData.CommandManager, but it was not found."); } return; } try { Quaternion identity = Quaternion.identity; expandWorldCommandRunMethod.Invoke(null, new object[3] { list, position, ((Quaternion)(ref identity)).eulerAngles }); } catch (Exception ex) { DropNSpawnPlugin.DropNSpawnLogger.LogWarning((object)("Failed to run event commands: " + ex.Message)); } } private static MethodInfo? GetExpandWorldCommandRunMethod() { if (ExpandWorldCommandRunMethodLookedUp) { return ExpandWorldCommandRunMethod; } ExpandWorldCommandRunMethodLookedUp = true; ExpandWorldCommandRunMethod = SafeTypeLookup.FindLoadedType("ExpandWorldData.CommandManager", "ExpandWorldData")?.GetMethods(BindingFlags.Static | BindingFlags.Public).FirstOrDefault((MethodInfo method) => method.Name == "Run" && method.GetParameters().Length == 3); return ExpandWorldCommandRunMethod; } private static EventRuntimeMetadata CloneMetadata(EventRuntimeMetadata metadata) { return new EventRuntimeMetadata { PlayerBase = ClonePlayerBaseCondition(metadata.PlayerBase), RequiredEnvironments = metadata.RequiredEnvironments?.ToList(), PlayerLimit = CloneRange(metadata.PlayerLimit), PlayerDistance = metadata.PlayerDistance, StartCommands = metadata.StartCommands?.ToList(), EndCommands = metadata.EndCommands?.ToList() }; } private static PlayerBaseCondition? ClonePlayerBaseCondition(PlayerBaseCondition? condition) { if (condition != null) { return new PlayerBaseCondition { AllowNear = condition.AllowNear, AllowAway = condition.AllowAway }; } return null; } private static IntRangeDefinition? CloneRange(IntRangeDefinition? range) { if (range == null) { return null; } int? min = range.Min; int? max = range.Max; RangeFormatting.NormalizeAscending(ref min, ref max); return new IntRangeDefinition { Min = min, Max = max }; } private static bool RangeContains(IntRangeDefinition range, int value, int fallbackMin, int fallbackMax) { int num = range.Min ?? fallbackMin; int num2 = range.Max ?? fallbackMax; if (num > num2) { int num3 = num2; num2 = num; num = num3; } if (value >= num) { return value <= num2; } return false; } private static List CloneEvents(IEnumerable events) { return events.Select((RandomEvent ev) => ev.Clone()).ToList(); } private static bool TryParsePlayerBaseCondition(List? values, string context, out PlayerBaseCondition? condition) { condition = null; if (values == null) { return false; } if (values.Count == 0) { return false; } PlayerBaseCondition playerBaseCondition = new PlayerBaseCondition(); foreach (string value in values) { if (string.Equals(value, "near", StringComparison.OrdinalIgnoreCase)) { playerBaseCondition.AllowNear = true; continue; } if (string.Equals(value, "away", StringComparison.OrdinalIgnoreCase)) { playerBaseCondition.AllowAway = true; continue; } DropNSpawnPlugin.DropNSpawnLogger.LogWarning((object)("Event entry '" + context + "' contains invalid conditions.playerBase value '" + value + "'. Use [near], [away], or [near, away].")); return false; } if (!playerBaseCondition.AllowNear && !playerBaseCondition.AllowAway) { return false; } condition = playerBaseCondition; return true; } private static bool TryParseBiomes(List names, string context, out Biome biomes) { //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_0076: Expected I4, but got Unknown biomes = (Biome)0; foreach (string name in names) { string text = (name ?? "").Trim(); if (text.Length != 0) { if (!BiomeResolutionSupport.TryResolveBiomeToken(text, out var biome)) { DropNSpawnPlugin.DropNSpawnLogger.LogWarning((object)("Event entry '" + context + "' contains unknown biome '" + text + "'.")); biomes = (Biome)0; return false; } biomes |= biome; } } return true; } private static List ConvertBiomes(Biome biomes) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) return BiomeResolutionSupport.ConvertBiomeMaskToNames(biomes); } private static List ConvertBiomeAreas(BiomeArea biomeAreas) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Invalid comparison between Unknown and I4 //IL_000b: 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_0047: 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_004a: Invalid comparison between Unknown and I4 //IL_004c: 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_004e: 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) if ((int)biomeAreas == 3) { return new List { ((object)(BiomeArea)3/*cast due to .constrained prefix*/).ToString() }; } List list = new List(); foreach (BiomeArea value in Enum.GetValues(typeof(BiomeArea))) { if ((int)value != 3 && (BiomeArea)(biomeAreas & value) == value) { list.Add(((object)value/*cast due to .constrained prefix*/).ToString()); } } return list; } private static List ResolveItemDrops(List names, string context) { List list = new List(); foreach (string name in names) { ItemDrop val = ResolveItemDrop(name); if ((Object)(object)val == (Object)null) { DropNSpawnPlugin.DropNSpawnLogger.LogWarning((object)("Event entry '" + context + "' references unknown player item '" + name + "'.")); } else { list.Add(val); } } return list; } private static ItemDrop? ResolveItemDrop(string name) { if (string.IsNullOrWhiteSpace(name)) { return null; } GameObject val = (((Object)(object)ObjectDB.instance != (Object)null) ? ObjectDB.instance.GetItemPrefab(name) : null); if ((Object)(object)val == (Object)null && (Object)(object)ZNetScene.instance != (Object)null) { val = ZNetScene.instance.GetPrefab(name); } if (!((Object)(object)val != (Object)null)) { return null; } return val.GetComponent(); } private static List ConvertItemDrops(List itemDrops) { return (from item in itemDrops ?? new List() where (Object)(object)item != (Object)null select (!((Object)(object)((Component)item).gameObject != (Object)null)) ? "" : ((Object)((Component)item).gameObject).name into name where name.Length > 0 select name).ToList(); } private static bool TryParseIntRange(string? raw, string context, string fieldName, out IntRangeDefinition range) { range = new IntRangeDefinition(); try { var (min, max) = RangeFormatting.ParseIntRange(raw); RangeFormatting.NormalizeAscending(ref min, ref max); range.Min = min; range.Max = max; return range.HasValues(); } catch (Exception ex) { DropNSpawnPlugin.DropNSpawnLogger.LogWarning((object)("Event entry '" + context + "' contains invalid " + fieldName + " range '" + raw + "': " + ex.Message)); return false; } } private static bool TryParseFloat(List values, int index, float fallback, string context, string fieldName, out float result) { result = fallback; if (index >= values.Count || string.IsNullOrWhiteSpace(values[index])) { return false; } if (float.TryParse(values[index], NumberStyles.Any, CultureInfo.InvariantCulture, out result)) { return true; } DropNSpawnPlugin.DropNSpawnLogger.LogWarning((object)("Event entry '" + context + "' contains invalid " + fieldName + " value '" + values[index] + "'.")); return false; } private static bool TryParseBool(List values, int index, bool fallback, string context, string fieldName, out bool result) { result = fallback; if (index >= values.Count || string.IsNullOrWhiteSpace(values[index])) { return false; } if (bool.TryParse(values[index], out result)) { return true; } DropNSpawnPlugin.DropNSpawnLogger.LogWarning((object)("Event entry '" + context + "' contains invalid " + fieldName + " value '" + values[index] + "'.")); return false; } private static string? NormalizeOptionalString(string? value) { if (value == null) { return null; } string text = value.Trim(); if (text.Length != 0) { return text; } return null; } private static string? NormalizeOptionalStringPreserveEmpty(string? value) { return value?.Trim(); } private static List? NormalizePositionalStringList(List? values) { if (values != null && values.Count != 0) { return values.Select((string value) => (value ?? "").Trim()).ToList(); } return null; } private static List? NormalizeOptionalStringList(List? values) { if (values == null) { return null; } List list = (from value in values select (value ?? "").Trim() into value where value.Length > 0 select value).ToList(); if (list.Count != 0) { return list; } return null; } private static string FormatFloat(float value) { return value.ToString("0.###", CultureInfo.InvariantCulture); } private static string? GetPrefabName(GameObject? prefab) { if (!((Object)(object)prefab == (Object)null)) { return ((Object)prefab).name; } return null; } private static string BuildDefaultOverrideYaml() { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine("# DropNSpawn event overrides."); stringBuilder.AppendLine("#"); stringBuilder.AppendLine("# Full schema example. The value before the first # is the fallback/default for a new event."); stringBuilder.AppendLine("# When overriding an existing event, omitted fields keep that event's baseline value."); stringBuilder.AppendLine("# The first inline comment value is an example override."); stringBuilder.AppendLine("#"); stringBuilder.AppendLine("# - event: '' # custom_greydwarf_raid # Event id/name. Required."); stringBuilder.AppendLine("# settings: [true, true, 60, 96, true] # [false, true, 120, 128, false] # enabled, random, duration, eventRange, pauseIfNoPlayerInArea; use '' to omit a middle value."); stringBuilder.AppendLine("# standalone: [0, 100] # [600, 20] # standaloneInterval seconds, standaloneChance percent."); stringBuilder.AppendLine("# spawnerDelay: 0 # 10 # Delay before event spawners can start spawning."); stringBuilder.AppendLine("# conditions:"); stringBuilder.AppendLine("# biomes: [] # [Meadows, BlackForest] # Biomes where this event can start. [] means any biome."); stringBuilder.AppendLine("# playerBase: [near, away] # [near] # Allowed player-base states. [] or omitted keeps the baseline value."); stringBuilder.AppendLine("# requiredEnvironments: [] # [ThunderStorm] # Event-level required environment names."); stringBuilder.AppendLine("# players: [0~, 100] # [1~4, 100] # Player count range, distance."); stringBuilder.AppendLine("# requiredGlobalKeys: [] # [defeated_eikthyr] # Required global keys."); stringBuilder.AppendLine("# forbiddenGlobalKeys: [] # [defeated_gdking] # Blocking global keys."); stringBuilder.AppendLine("# requiredKnownItems: [] # [CryptKey] # Known item prefabs required for PlayerEvents gating."); stringBuilder.AppendLine("# forbiddenKnownItems: [] # [Wishbone] # Known item prefabs that block PlayerEvents gating."); stringBuilder.AppendLine("# requiredPlayerKeysAny: [] # [KilledTroll] # Any one player key can satisfy PlayerEvents gating."); stringBuilder.AppendLine("# requiredPlayerKeysAll: [] # [KilledTroll, defeated_eikthyr] # All listed player keys are required."); stringBuilder.AppendLine("# forbiddenPlayerKeys: [] # [defeated_dragon] # Player keys that block PlayerEvents gating."); stringBuilder.AppendLine("# messages: ['', ''] # ['$event_gdkingarmy_start', '$event_gdkingarmy_end'] # startMessage, endMessage."); stringBuilder.AppendLine("# forceEnvironment: '' # GDKing # Force an environment while the event is active."); stringBuilder.AppendLine("# forceMusic: '' # boss_gdking # Force music while the event is active."); stringBuilder.AppendLine("# startCommands: [] # ['say Event started'] # ExpandWorldData commands run when the event starts."); stringBuilder.AppendLine("# endCommands: [] # ['say Event ended'] # ExpandWorldData commands run when the event ends."); stringBuilder.AppendLine("# spawns: [] # See spawn entry schema below. [] means no spawns for a new event."); stringBuilder.AppendLine("#"); stringBuilder.AppendLine("# Advanced standalone/command example. These fields are valid in overrides but are omitted from generated reference files."); stringBuilder.AppendLine("# - event: custom_greydwarf_raid"); stringBuilder.AppendLine("# standalone: [600, 20]"); stringBuilder.AppendLine("# spawnerDelay: 10"); stringBuilder.AppendLine("# startCommands: ['say Event started']"); stringBuilder.AppendLine("# endCommands: ['say Event ended']"); stringBuilder.AppendLine("#"); stringBuilder.AppendLine("# Spawn entry schema. Values before # are SpawnSystem.SpawnData defaults for one entry."); stringBuilder.AppendLine("# All per-spawn fields are flat under spawnSystem; nested conditions and modifiers blocks are not supported."); stringBuilder.AppendLine("# - event: custom_greydwarf_raid"); stringBuilder.AppendLine("# spawns:"); stringBuilder.AppendLine("# - prefab: '' # Greydwarf # Creature prefab to spawn. Required for a spawn entry."); stringBuilder.AppendLine("# enabled: true # false # Enables this spawn entry."); stringBuilder.AppendLine("# spawnSystem:"); stringBuilder.AppendLine("# name: '' # Greydwarf raid scout # Optional display/debug name."); stringBuilder.AppendLine("# huntPlayer: false # true # Force spawned creatures to hunt players."); stringBuilder.AppendLine("# level: 1 # 1~2 # Min~max creature level."); stringBuilder.AppendLine("# overrideLevelUpChance: -1 # 25 # Override level-up chance percent. -1 uses vanilla/default."); stringBuilder.AppendLine("# levelUpMinCenterDistance: 0 # 2000 # Min distance from world center for level-up."); stringBuilder.AppendLine("# groundOffset: 0.5 # 0 # Vertical spawn offset."); stringBuilder.AppendLine("# groundOffsetRandom: 0 # 1 # Random additional vertical offset."); stringBuilder.AppendLine("# spawnInterval: 4 # 10 # Seconds between spawn checks."); stringBuilder.AppendLine("# spawnChance: 100 # 50 # Chance percent per interval."); stringBuilder.AppendLine("# spawnRadius: 40~80 # 20~60 # Min~max spawn radius. Native effective default; 0 on min/max uses the native 40/80 fallback."); stringBuilder.AppendLine("# groupSize: 1 # 1~3 # Min~max group size."); stringBuilder.AppendLine("# groupRadius: 3 # 6 # Radius around the group center."); stringBuilder.AppendLine("# noSpawnRadius: 10 # 20 # Minimum distance to another instance of this prefab."); stringBuilder.AppendLine("# maxSpawned: 1 # 6 # Max active event creatures from this entry."); stringBuilder.AppendLine("# tilt: 0~35 # 0~25 # Terrain tilt range."); stringBuilder.AppendLine("# altitude: -1000~1000 # 1~1000 # Altitude range."); stringBuilder.AppendLine("# oceanDepth: 0~0 # 1~30 # Ocean depth range."); stringBuilder.AppendLine("# distanceFromCenter: 0~0 # 500~ # Distance from world center."); stringBuilder.AppendLine("# biomes: [] # [Meadows] # Spawn-entry biome filter. [] means no extra entry filter."); stringBuilder.AppendLine("# biomeAreas: [Everything] # [Median] # Biome area filter."); stringBuilder.AppendLine("# timeOfDay: [day, night] # [night] # Time of day filter."); stringBuilder.AppendLine("# requiredEnvironments: [] # [Rain] # Spawn-entry required environments."); stringBuilder.AppendLine("# requiredGlobalKey: '' # defeated_eikthyr # Required global key for this spawn entry."); stringBuilder.AppendLine("# inLava: # true # true = only lava, false = outside lava, empty = vanilla both-state default."); stringBuilder.AppendLine("# inForest: # true # true = only forest, false = outside forest, empty = vanilla both-state default."); stringBuilder.AppendLine("# insidePlayerBase: false # true # Spawn inside player base."); stringBuilder.AppendLine("# canSpawnCloseToPlayer: false # true # Allow spawn close to players."); stringBuilder.AppendLine("# fields: {} # { m_character.m_faction: ForestMonsters } # Raw field overrides."); stringBuilder.AppendLine("# objects: [] # [Wood,0,0,0,1] # ExpandWorldData object entries."); stringBuilder.AppendLine("# data: '' # my_spawn_data # ExpandWorldData data entry name."); stringBuilder.AppendLine("# faction: '' # ForestMonsters # ExpandWorldData faction override."); return stringBuilder.ToString(); } } internal static class ExampleContentWriter { private const string CharacterSampleFileName = "DNS_character.sample.yml"; private const string ObjectSampleFileName = "DNS_object.sample.yml"; private const string SpawnerSampleFileName = "DNS_spawner.sample.yml"; private const string SpawnSystemSampleFileName = "DNS_spawnsystem.sample.yml"; private const string ReadmeContent = "Across all field types, `field: null` or omitting the field usually means the field is unspecified.\n\nMore broadly, it falls back to that field's default behavior, and the exact effect depends on the field.\n\nFor list, map/object, and string fields respectively, `[]`, `{}`, and `''` explicitly assign an empty list, empty object, or empty string, and their exact effect depends on the field.\n\n# `biomes: then indented - BlackForest and - Mistlands on separate lines` is another supported format for Block list\n\nRange shorthand uses 1, 1~5, and ~5. Note: 1~ currently resolves to 1 because open-ended max is not supported.\n\nfaction accepts vanilla Character.Faction names and ExpandWorldFactions custom faction names.\n"; private const string CharacterConditionContent = "# Character-domain coexistence samples.\n# Safe here as a .sample.yml file. Copy rows into the active override file, or rename this file to a DNS__*.yml name to load it.\n# Character conditions are top-level only.\n# Every matching entry for the same prefab contributes drops.\n# Identical drop rows are deduped.\n\n- prefab: Boar\n enabled: true\n conditions:\n biomes: [Meadows]\n characterDrop:\n drops:\n - item: LeatherScraps\n amount: 1~2\n chance: 1\n\n- prefab: Boar\n enabled: true\n conditions:\n biomes: [Meadows]\n timeOfDay: [night]\n characterDrop:\n drops:\n - item: Coins\n amount: 1~3\n chance: 0.25\n\n- prefab: Skeleton\n enabled: true\n conditions:\n biomes: [BlackForest]\n requiredGlobalKeys: [defeated_gdking]\n characterDrop:\n drops:\n - item: BoneFragments\n amount: 2~4\n chance: 1\n"; private const string ObjectConditionContent = "# Object-domain coexistence progression\n# Safe here as a .sample.yml file. Copy rows into the active override file, or rename this file to a DNS__*.yml name to load it.\n# Drop-table style payloads merge matching rows.\n# Scalar-style payloads are applied in order, so later matching entries override earlier values.\n\n- prefab: TreasureChest_meadows\n enabled: true\n conditions:\n biomes: [Meadows]\n container:\n rolls: 1~2\n drops:\n - item: Coins\n stack: 10~20\n weight: 1\n\n- prefab: RaspberryBush\n enabled: true\n conditions:\n biomes: [Meadows]\n pickable:\n overrideName: Berry Bush\n drop:\n item: Raspberry\n amount: 3\n minAmountScaled: 1\n\n- prefab: MineRock_Copper\n enabled: true\n conditions:\n locations: [CopperDeposit]\n mineRock:\n rolls: 2~3\n drops:\n - item: CopperOre\n stack: 2~4\n weight: 1\n"; private const string SpawnerConditionContent = "# Spawner-domain coexistence samples.\n# Safe here as a .sample.yml file. Copy rows into the active override file, or rename this file to a DNS__*.yml name to load it.\n# Use top-level `locations` to scope one location-bound spawner to one or more locations.\n# Only the most specific passing entry is applied.\n\n- prefab: Spawner_GreydwarfNest\n enabled: true\n spawnArea:\n spawnInterval: 30\n maxNear: 4\n creatures:\n - creature: Greydwarf\n weight: 1\n\n- prefab: Spawner_GreydwarfNest\n enabled: true\n locations: [Greydwarf_camp1]\n spawnArea:\n spawnInterval: 15\n maxNear: 6\n creatures:\n - creature: Greydwarf_Elite\n weight: 1\n\n- prefab: Spawner_Boar\n enabled: true\n creatureSpawner:\n creature: Boar\n level: 1~2\n spawnCheckInterval: 5\n triggerDistance: 60\n"; private const string SpawnSystemContent = "# SpawnSystem-domain coexistence samples.\n# Safe here as a .sample.yml file. Copy rows into DNS_spawnsystem.yml one by one.\n# IMPORTANT: spawnsystem overrides replace the live world spawn table with the rows you load.\n# Every loaded entry becomes its own native SpawnSystem row.\n# Optional first item for this file only:\n# - spawnIntervalMultiplier: 2.0\n# The multiplier must be greater than zero. 0.5 makes checks more frequent; 2.0 makes them less frequent.\n\n- prefab: Boar\n enabled: true\n spawnSystem:\n name: Boar meadows day\n spawnInterval: 150\n spawnChance: 50\n groupSize: 1~3\n groupRadius: 5\n altitude: 0~1000\n biomes: [Meadows]\n biomeAreas: [Edge, Median]\n timeOfDay: [day]\n\n- prefab: Neck\n enabled: true\n spawnSystem:\n name: Neck rain\n spawnInterval: 120\n spawnChance: 40\n groupSize: 2~4\n groupRadius: 5\n altitude: -1.5~0.5\n biomes: [Meadows]\n requiredEnvironments: [Rain, LightRain]\n\n- prefab: Greydwarf\n enabled: true\n spawnSystem:\n name: Greydwarf night\n spawnInterval: 120\n spawnChance: 30\n groupSize: 2~3\n biomes: [BlackForest]\n timeOfDay: [night]\n faction: ForestMonsters\n"; private static string ExamplesDirectoryPath => Path.Combine(DropNSpawnPlugin.YamlConfigDirectoryPath, "examples"); internal static void EnsureDefaultExampleFiles() { EnsureExampleFile("README.md", "Across all field types, `field: null` or omitting the field usually means the field is unspecified.\n\nMore broadly, it falls back to that field's default behavior, and the exact effect depends on the field.\n\nFor list, map/object, and string fields respectively, `[]`, `{}`, and `''` explicitly assign an empty list, empty object, or empty string, and their exact effect depends on the field.\n\n# `biomes: then indented - BlackForest and - Mistlands on separate lines` is another supported format for Block list\n\nRange shorthand uses 1, 1~5, and ~5. Note: 1~ currently resolves to 1 because open-ended max is not supported.\n\nfaction accepts vanilla Character.Faction names and ExpandWorldFactions custom faction names.\n"); EnsureExampleFile("DNS_character.sample.yml", "# Character-domain coexistence samples.\n# Safe here as a .sample.yml file. Copy rows into the active override file, or rename this file to a DNS__*.yml name to load it.\n# Character conditions are top-level only.\n# Every matching entry for the same prefab contributes drops.\n# Identical drop rows are deduped.\n\n- prefab: Boar\n enabled: true\n conditions:\n biomes: [Meadows]\n characterDrop:\n drops:\n - item: LeatherScraps\n amount: 1~2\n chance: 1\n\n- prefab: Boar\n enabled: true\n conditions:\n biomes: [Meadows]\n timeOfDay: [night]\n characterDrop:\n drops:\n - item: Coins\n amount: 1~3\n chance: 0.25\n\n- prefab: Skeleton\n enabled: true\n conditions:\n biomes: [BlackForest]\n requiredGlobalKeys: [defeated_gdking]\n characterDrop:\n drops:\n - item: BoneFragments\n amount: 2~4\n chance: 1\n"); EnsureExampleFile("DNS_object.sample.yml", "# Object-domain coexistence progression\n# Safe here as a .sample.yml file. Copy rows into the active override file, or rename this file to a DNS__*.yml name to load it.\n# Drop-table style payloads merge matching rows.\n# Scalar-style payloads are applied in order, so later matching entries override earlier values.\n\n- prefab: TreasureChest_meadows\n enabled: true\n conditions:\n biomes: [Meadows]\n container:\n rolls: 1~2\n drops:\n - item: Coins\n stack: 10~20\n weight: 1\n\n- prefab: RaspberryBush\n enabled: true\n conditions:\n biomes: [Meadows]\n pickable:\n overrideName: Berry Bush\n drop:\n item: Raspberry\n amount: 3\n minAmountScaled: 1\n\n- prefab: MineRock_Copper\n enabled: true\n conditions:\n locations: [CopperDeposit]\n mineRock:\n rolls: 2~3\n drops:\n - item: CopperOre\n stack: 2~4\n weight: 1\n"); EnsureExampleFile("DNS_spawner.sample.yml", "# Spawner-domain coexistence samples.\n# Safe here as a .sample.yml file. Copy rows into the active override file, or rename this file to a DNS__*.yml name to load it.\n# Use top-level `locations` to scope one location-bound spawner to one or more locations.\n# Only the most specific passing entry is applied.\n\n- prefab: Spawner_GreydwarfNest\n enabled: true\n spawnArea:\n spawnInterval: 30\n maxNear: 4\n creatures:\n - creature: Greydwarf\n weight: 1\n\n- prefab: Spawner_GreydwarfNest\n enabled: true\n locations: [Greydwarf_camp1]\n spawnArea:\n spawnInterval: 15\n maxNear: 6\n creatures:\n - creature: Greydwarf_Elite\n weight: 1\n\n- prefab: Spawner_Boar\n enabled: true\n creatureSpawner:\n creature: Boar\n level: 1~2\n spawnCheckInterval: 5\n triggerDistance: 60\n"); EnsureExampleFile("DNS_spawnsystem.sample.yml", "# SpawnSystem-domain coexistence samples.\n# Safe here as a .sample.yml file. Copy rows into DNS_spawnsystem.yml one by one.\n# IMPORTANT: spawnsystem overrides replace the live world spawn table with the rows you load.\n# Every loaded entry becomes its own native SpawnSystem row.\n# Optional first item for this file only:\n# - spawnIntervalMultiplier: 2.0\n# The multiplier must be greater than zero. 0.5 makes checks more frequent; 2.0 makes them less frequent.\n\n- prefab: Boar\n enabled: true\n spawnSystem:\n name: Boar meadows day\n spawnInterval: 150\n spawnChance: 50\n groupSize: 1~3\n groupRadius: 5\n altitude: 0~1000\n biomes: [Meadows]\n biomeAreas: [Edge, Median]\n timeOfDay: [day]\n\n- prefab: Neck\n enabled: true\n spawnSystem:\n name: Neck rain\n spawnInterval: 120\n spawnChance: 40\n groupSize: 2~4\n groupRadius: 5\n altitude: -1.5~0.5\n biomes: [Meadows]\n requiredEnvironments: [Rain, LightRain]\n\n- prefab: Greydwarf\n enabled: true\n spawnSystem:\n name: Greydwarf night\n spawnInterval: 120\n spawnChance: 30\n groupSize: 2~3\n biomes: [BlackForest]\n timeOfDay: [night]\n faction: ForestMonsters\n"); } private static void EnsureExampleFile(string fileName, string defaultContent) { string path = Path.Combine(ExamplesDirectoryPath, fileName); if (!File.Exists(path)) { GeneratedArtifactWriter.WriteTextAlways(path, defaultContent); } } } internal static class FactionIntegration { private enum FactionProvider { Native, CreatureManager, ExpandWorldFactions } private readonly struct ResolvedFaction { internal Faction Value { get; } internal FactionProvider Provider { get; } internal ResolvedFaction(Faction value, FactionProvider provider) { //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) Value = value; Provider = provider; } } private static readonly string NativeFactionList = string.Join(", ", Enum.GetNames(typeof(Faction))); private static readonly object Sync = new object(); private static readonly HashSet WarningCache = new HashSet(StringComparer.OrdinalIgnoreCase); private static readonly int HashFaction = StringExtensionMethods.GetStableHashCode("faction"); private static readonly FieldRef BaseAiNviewRef = AccessTools.FieldRefAccess("m_nview"); private static readonly FieldRef BaseAiCharacterRef = AccessTools.FieldRefAccess("m_character"); private static bool _creatureManagerApiResolved; private static MethodInfo? _creatureManagerTryResolveMethod; private static MethodInfo? _creatureManagerTryApplyMethod; private static MethodInfo? _creatureManagerGetNamesMethod; private static bool _expandWorldFactionsApiResolved; private static MethodInfo? _expandWorldFactionsTryGetFactionMethod; private static MethodInfo? _expandWorldFactionsBaseAiSetupMethod; internal static bool HasFaction(string? configuredFaction) { return !string.IsNullOrWhiteSpace(configuredFaction); } internal static string? Normalize(string? configuredFaction) { string text = (configuredFaction ?? "").Trim(); if (text.Length <= 0) { return null; } return text; } internal static string GetNativeFactionList() { List list = new List(Enum.GetNames(typeof(Faction))); HashSet hashSet = new HashSet(list, StringComparer.OrdinalIgnoreCase); foreach (string creatureManagerFactionName in GetCreatureManagerFactionNames()) { if (hashSet.Add(creatureManagerFactionName)) { list.Add(creatureManagerFactionName); } } if (list.Count != Enum.GetNames(typeof(Faction)).Length) { return string.Join(", ", list); } return NativeFactionList; } internal static bool Matches(Faction currentFaction, string? configuredFaction) { //IL_0014: 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) string text = Normalize(configuredFaction); if (text != null && TryResolveFaction(text, out var resolvedFaction)) { return currentFaction == resolvedFaction.Value; } return false; } internal static void Apply(Character? character, string? configuredFaction, string context) { //IL_00d8: Unknown result type (might be due to invalid IL or missing references) //IL_00dd: Unknown result type (might be due to invalid IL or missing references) //IL_00e6: Unknown result type (might be due to invalid IL or missing references) //IL_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_008f: Unknown result type (might be due to invalid IL or missing references) string text = Normalize(configuredFaction); if ((Object)(object)character == (Object)null || text == null) { return; } if (!TryResolveFaction(text, out var resolvedFaction)) { WarnOnce("invalid-faction:" + text, "Entry '" + context + "' uses invalid faction '" + text + "'. Use a vanilla Character.Faction value, a CreatureManager registered faction, or an ExpandWorldFactions custom faction name. Available vanilla and CreatureManager names: " + GetNativeFactionList() + "."); } else if (resolvedFaction.Provider == FactionProvider.CreatureManager) { if (!TryInvokeCreatureManagerApply(character, text)) { character.m_faction = resolvedFaction.Value; WriteFactionToZdo(character, text, resolvedFaction.Value); WarnOnce("creature-manager-faction-apply:" + text, "CreatureManager resolved faction '" + text + "' for entry '" + context + "', but its apply call failed. The faction id was applied without CreatureManager's BaseAI refresh."); } } else { character.m_faction = resolvedFaction.Value; WriteFactionToZdo(character, text, resolvedFaction.Value); RefreshBaseAi(((Component)character).GetComponent(), text, resolvedFaction); } } private static void WriteFactionToZdo(Character character, string configuredFaction, Faction faction) { //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Expected I4, but got Unknown ZNetView component = ((Component)character).GetComponent(); ZDO val = ((component != null) ? component.GetZDO() : null); if (val != null) { val.Set(HashFaction, configuredFaction); val.Set(HashFaction, (int)faction, false); } } internal static void ApplyFromZdo(BaseAI? baseAi) { //IL_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_00b7: Unknown result type (might be due to invalid IL or missing references) //IL_0132: Unknown result type (might be due to invalid IL or missing references) //IL_00f6: Unknown result type (might be due to invalid IL or missing references) //IL_00f8: 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_0087: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)baseAi == (Object)null) { return; } Character val = BaseAiCharacterRef.Invoke(baseAi); if ((Object)(object)val == (Object)null) { return; } ZNetView obj = BaseAiNviewRef.Invoke(baseAi); ZDO val2 = ((obj != null) ? obj.GetZDO() : null); if (val2 == null) { return; } string text = val2.GetString(HashFaction, ""); if (!string.IsNullOrWhiteSpace(text)) { string text2 = text.Trim(); if (TryResolveFaction(text2, out var resolvedFaction)) { if (resolvedFaction.Provider == FactionProvider.CreatureManager) { if (!TryInvokeCreatureManagerApply(val, text2)) { val.m_faction = resolvedFaction.Value; WarnOnce("creature-manager-zdo-faction-apply:" + text2, "CreatureManager resolved ZDO faction '" + text2 + "', but its apply call failed. The faction id was applied without CreatureManager's BaseAI refresh."); } } else { val.m_faction = resolvedFaction.Value; RefreshBaseAi(baseAi, text2, resolvedFaction); } return; } } int num = val2.GetInt(HashFaction, 0); if (num != 0) { string text3 = num.ToString(); if (TryInvokeCreatureManagerResolve(text3, out var faction)) { if (!TryInvokeCreatureManagerApply(val, text3)) { val.m_faction = faction; WarnOnce("creature-manager-zdo-faction-apply:" + text3, "CreatureManager resolved ZDO faction id '" + text3 + "', but its apply call failed. The faction id was applied without CreatureManager's BaseAI refresh."); } } else if (!string.IsNullOrWhiteSpace(text) || !TryInvokeExpandWorldFactionsSetup(baseAi)) { val.m_faction = (Faction)num; } } else if (!string.IsNullOrWhiteSpace(text)) { WarnOnce("invalid-zdo-faction:" + text.Trim(), "Spawned creature contains unknown faction '" + text.Trim() + "' in its ZDO. Available vanilla and CreatureManager names: " + GetNativeFactionList() + "."); } } private static void RefreshBaseAi(BaseAI? baseAi, string configuredFaction, ResolvedFaction resolvedFaction) { //IL_0027: 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) if (!((Object)(object)baseAi == (Object)null) && (resolvedFaction.Provider == FactionProvider.ExpandWorldFactions || (resolvedFaction.Provider == FactionProvider.Native && TryInvokeExpandWorldFactionsResolve(configuredFaction, out var faction) && faction == resolvedFaction.Value))) { TryInvokeExpandWorldFactionsSetup(baseAi); } } private static bool TryResolveFaction(string configuredFaction, out ResolvedFaction resolvedFaction) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) resolvedFaction = default(ResolvedFaction); if (TryInvokeCreatureManagerResolve(configuredFaction, out var faction)) { resolvedFaction = new ResolvedFaction(faction, FactionProvider.CreatureManager); return true; } if (Enum.TryParse(configuredFaction, ignoreCase: true, out Faction result)) { resolvedFaction = new ResolvedFaction(result, FactionProvider.Native); return true; } if (TryInvokeExpandWorldFactionsResolve(configuredFaction, out var faction2)) { resolvedFaction = new ResolvedFaction(faction2, FactionProvider.ExpandWorldFactions); return true; } return false; } private static bool TryInvokeCreatureManagerResolve(string configuredFaction, out Faction faction) { //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_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Expected I4, but got Unknown faction = (Faction)0; lock (Sync) { TryResolveCreatureManagerApi(); if (_creatureManagerTryResolveMethod == null) { return false; } try { object[] array = new object[2] { configuredFaction, null }; object obj = _creatureManagerTryResolveMethod.Invoke(null, array); if (obj is bool && (bool)obj && array[1] is Faction val) { faction = (Faction)(int)val; return true; } } catch (Exception exception) { WarnOnce("creature-manager-faction-resolve-error", "Failed to resolve CreatureManager faction names. " + GetExceptionMessage(exception)); } return false; } } private static bool TryInvokeCreatureManagerApply(Character character, string configuredFaction) { lock (Sync) { TryResolveCreatureManagerApi(); if (_creatureManagerTryApplyMethod == null) { return false; } try { object obj = _creatureManagerTryApplyMethod.Invoke(null, new object[2] { character, configuredFaction }); return obj is bool && (bool)obj; } catch (Exception exception) { WarnOnce("creature-manager-faction-apply-error", "Failed to apply a CreatureManager faction. " + GetExceptionMessage(exception)); return false; } } } private static IEnumerable GetCreatureManagerFactionNames() { lock (Sync) { TryResolveCreatureManagerApi(); if (_creatureManagerGetNamesMethod == null) { return Array.Empty(); } try { if (_creatureManagerGetNamesMethod.Invoke(null, Array.Empty()) is IEnumerable collection) { return new List(collection); } } catch (Exception exception) { WarnOnce("creature-manager-faction-names-error", "Failed to read CreatureManager faction names. " + GetExceptionMessage(exception)); } return Array.Empty(); } } private static bool TryInvokeExpandWorldFactionsResolve(string configuredFaction, out Faction faction) { //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_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Expected I4, but got Unknown faction = (Faction)0; lock (Sync) { TryResolveExpandWorldFactionsApi(); if (_expandWorldFactionsTryGetFactionMethod == null) { return false; } try { object[] array = new object[2] { configuredFaction, null }; object obj = _expandWorldFactionsTryGetFactionMethod.Invoke(null, array); if (obj is bool && (bool)obj && array[1] is Faction val) { faction = (Faction)(int)val; return true; } } catch (Exception exception) { WarnOnce("expand-world-faction-resolve-error", "Failed to resolve ExpandWorldFactions faction names. " + GetExceptionMessage(exception)); } return false; } } private static bool TryInvokeExpandWorldFactionsSetup(BaseAI baseAi) { lock (Sync) { TryResolveExpandWorldFactionsApi(); if (_expandWorldFactionsBaseAiSetupMethod == null) { return false; } try { _expandWorldFactionsBaseAiSetupMethod.Invoke(null, new object[1] { baseAi }); return true; } catch (Exception exception) { WarnOnce("expand-world-faction-setup-error", "Failed to refresh ExpandWorldFactions BaseAI state. " + GetExceptionMessage(exception)); return false; } } } private static void TryResolveCreatureManagerApi() { if (!_creatureManagerApiResolved) { _creatureManagerApiResolved = true; Type type = SafeTypeLookup.FindLoadedType("CreatureManager.CreatureManagerFactionApi", "CreatureManager"); if (!(type == null)) { _creatureManagerTryResolveMethod = type.GetMethod("TryResolve", BindingFlags.Static | BindingFlags.Public, null, new Type[2] { typeof(string), typeof(Faction).MakeByRefType() }, null); _creatureManagerTryApplyMethod = type.GetMethod("TryApply", BindingFlags.Static | BindingFlags.Public, null, new Type[2] { typeof(Character), typeof(string) }, null); _creatureManagerGetNamesMethod = type.GetMethod("GetNames", BindingFlags.Static | BindingFlags.Public, null, Type.EmptyTypes, null); } } } private static void TryResolveExpandWorldFactionsApi() { if (_expandWorldFactionsApiResolved) { return; } _expandWorldFactionsApiResolved = true; Type type = SafeTypeLookup.FindLoadedType("ExpandWorldData.Factions.FactionManager", "ExpandWorldFactions"); if (type != null) { MethodInfo[] methods = type.GetMethods(BindingFlags.Static | BindingFlags.Public); foreach (MethodInfo methodInfo in methods) { if (!(methodInfo.Name != "TryGetFaction")) { ParameterInfo[] parameters = methodInfo.GetParameters(); if (parameters.Length == 2 && parameters[0].ParameterType == typeof(string) && parameters[1].IsOut) { _expandWorldFactionsTryGetFactionMethod = methodInfo; break; } } } } _expandWorldFactionsBaseAiSetupMethod = SafeTypeLookup.FindLoadedType("ExpandWorldData.Factions.BaseAIAwake", "ExpandWorldFactions")?.GetMethod("Setup", BindingFlags.Static | BindingFlags.Public, null, new Type[1] { typeof(BaseAI) }, null); } private static string GetExceptionMessage(Exception exception) { if (exception is TargetInvocationException) { Exception innerException = exception.InnerException; if (innerException != null) { return innerException.Message; } } return exception.Message; } private static void WarnOnce(string key, string message) { if (WarningCache.Add(key)) { DropNSpawnPlugin.DropNSpawnLogger.LogWarning((object)message); } } } internal static class NetworkPayloadSyncSupport { private readonly struct DomainTransportPolicy { public int PublishedCacheBudgetBytes { get; } public bool UsesLargeRequestLane { get; } public bool EnableArtifactPrewarm { get; } public DomainTransportPolicy(int publishedCacheBudgetBytes, bool usesLargeRequestLane, bool enableArtifactPrewarm) { PublishedCacheBudgetBytes = Math.Max(0, publishedCacheBudgetBytes); UsesLargeRequestLane = usesLargeRequestLane; EnableArtifactPrewarm = enableArtifactPrewarm; } } private sealed class PayloadManifest { public string Hash { get; set; } = ""; public int CompressedSize { get; set; } public int ChunkCount { get; set; } public int? EntryCount { get; set; } public bool IsEmpty => Hash.Length == 0; public string Serialize() { if (!IsEmpty) { return string.Format(CultureInfo.InvariantCulture, "v2|{0}|{1}|{2}|{3}", Hash, CompressedSize, ChunkCount, Math.Max(0, EntryCount.GetValueOrDefault())); } return ""; } public static bool TryParse(string? raw, out PayloadManifest manifest) { manifest = new PayloadManifest(); if (string.IsNullOrWhiteSpace(raw)) { return true; } string[] array = (raw ?? "").Split('|'); if (array.Length != 5 || !string.Equals(array[0], "v2", StringComparison.Ordinal)) { return false; } if (!int.TryParse(array[2], NumberStyles.Integer, CultureInfo.InvariantCulture, out var result) || !int.TryParse(array[3], NumberStyles.Integer, CultureInfo.InvariantCulture, out var result2)) { return false; } if (!int.TryParse(array[4], NumberStyles.Integer, CultureInfo.InvariantCulture, out var result3)) { return false; } if (!IsValidPayloadHash(array[1]) || result <= 0 || result > 67108864 || result2 <= 0 || result3 < 0) { return false; } manifest = new PayloadManifest { Hash = array[1], CompressedSize = result, ChunkCount = result2, EntryCount = result3 }; return true; } } private sealed class PayloadSignatureBuilder { private readonly StringBuilder _builder = new StringBuilder(); internal void WriteBool(bool value) { _builder.Append(value ? "T;" : "F;"); } internal void WriteInt(int value) { _builder.Append('I').Append(value.ToString(CultureInfo.InvariantCulture)).Append(';'); } internal void WriteFloat(float value) { _builder.Append('R').Append(value.ToString("R", CultureInfo.InvariantCulture)).Append(';'); } internal void WriteString(string value) { string text = value ?? ""; _builder.Append('S').Append(text.Length.ToString(CultureInfo.InvariantCulture)).Append(':') .Append(text) .Append(';'); } internal string ComputeHash() { return ReferenceRefreshSupport.ComputeStableHash(_builder.ToString()); } } private interface IDomainTransport { string DomainKey { get; } bool UsesLargeRequestLane { get; } int ClientRequestPriority { get; } int PublishedCacheBytes { get; } int PublishedPayloadHistoryItemCount { get; } int PublishedTransferArtifactCount { get; } bool HasWaitingRequest(); int CountClientRequestInFlight(bool usesLargeRequestLane); bool TryStartDesiredPayloadRequest(); void UpdatePinnedOutboundTransferReferences(OutboundTransferState? previousState, OutboundTransferState? nextState); void EnqueuePublishedPayloadChunks(long sender, string requestedHash, string baseHash, long requestId); void ReceivePayloadChunk(long sender, string hash, long requestId, int transferKind, string baseHash, int chunkIndex, int chunkCount, int compressedSize, byte[] chunkBytes); bool TryTrimPublishedCacheOnce(); void ClearPinnedCacheReferences(); void ResetState(); } private sealed class DomainTransport : IDomainTransport { public string LastPublishedSignature = ""; public string LastPublishedManifest = ""; public byte[]? PublishedPayloadBytes; public byte[]? PublishedCompressedBytes; public PayloadEntryIndex? PublishedPayloadIndex; public PayloadManifest PublishedPayloadManifest = new PayloadManifest(); public int PublishedPayloadHistoryBytes; public string DesiredManifest = ""; public PayloadManifest DesiredPayloadManifest = new PayloadManifest(); public string InvalidDesiredManifest = ""; public string BlockedManifestHash = ""; public string BlockedManifestReason = ""; public string FullFallbackAttemptedHash = ""; public string AvailableHash = ""; public byte[]? AvailablePayloadBytes; public List? AvailableEntries; public PayloadEntryIndex? AvailablePayloadIndex; public PendingInboundTransfer? PendingTransfer; public long NextRequestId; public bool RequestInFlight; public float RequestStartedAt; public bool ProcessingInFlight; public string ProcessingHash = ""; public int ProcessingVersion; public int PublishedTransferArtifactBytes; public int PublishedCacheBytes; public int PublishVersion; public bool PublishInFlight; public string PendingPublishedSignature = ""; public DomainTransportMetadata Metadata { get; } public DomainTransportPolicy TransportPolicy { get; } public string DomainKey { get; } public string DisplayName { get; } public string CacheDirectoryName { get; } public bool UsesLargeRequestLane { get; } public int PublishedCacheBudgetBytes { get; } public int ChunkSizeBytes => 240000; public bool EnableArtifactPrewarm { get; } public DomainCodec Codec { get; } public Func, string> SignatureBuilder { get; } public Func EntrySignatureBuilder { get; } public Func, byte[]> Serializer { get; } public Func> Deserializer { get; } public Func KeySelector { get; } public Action ReloadAction { get; } public Dictionary> PublishedPayloadHistory { get; } = new Dictionary>(StringComparer.Ordinal); public LinkedList PublishedPayloadHistoryLru { get; } = new LinkedList(); public Dictionary PublishedTransferArtifacts { get; } = new Dictionary(StringComparer.Ordinal); public LinkedList PublishedTransferArtifactLru { get; } = new LinkedList(); public Dictionary PinnedPayloadHashRefCounts { get; } = new Dictionary(StringComparer.Ordinal); public Dictionary PinnedArtifactKeyRefCounts { get; } = new Dictionary(StringComparer.Ordinal); public HashSet PendingArtifactBuildKeys { get; } = new HashSet(StringComparer.Ordinal); int IDomainTransport.PublishedCacheBytes => PublishedCacheBytes; bool IDomainTransport.UsesLargeRequestLane => UsesLargeRequestLane; int IDomainTransport.ClientRequestPriority => Metadata.ClientRequestPriority; int IDomainTransport.PublishedPayloadHistoryItemCount => PublishedPayloadHistory.Count; int IDomainTransport.PublishedTransferArtifactCount => PublishedTransferArtifacts.Count; public DomainTransport(DomainTransportMetadata metadata, DomainCodec codec) { Metadata = metadata ?? throw new ArgumentNullException("metadata"); TransportPolicy = ResolveTransportPolicy(metadata.TransportProfile); DomainKey = metadata.DomainKey; DisplayName = metadata.DisplayName; CacheDirectoryName = metadata.CacheDirectoryName; UsesLargeRequestLane = TransportPolicy.UsesLargeRequestLane; PublishedCacheBudgetBytes = TransportPolicy.PublishedCacheBudgetBytes; EnableArtifactPrewarm = TransportPolicy.EnableArtifactPrewarm; Codec = codec; SignatureBuilder = codec.SignatureBuilder; EntrySignatureBuilder = codec.EntrySignatureBuilder; Serializer = codec.Serializer; Deserializer = codec.Deserializer; KeySelector = metadata.KeySelector; ReloadAction = metadata.ApplyPayloadAction; } bool IDomainTransport.HasWaitingRequest() { return HasWaitingRequestLocked(this); } int IDomainTransport.CountClientRequestInFlight(bool usesLargeRequestLane) { return CountClientRequestInFlightLocked(this, usesLargeRequestLane); } bool IDomainTransport.TryStartDesiredPayloadRequest() { return TryStartDesiredPayloadRequestLocked(this); } void IDomainTransport.UpdatePinnedOutboundTransferReferences(OutboundTransferState? previousState, OutboundTransferState? nextState) { UpdatePinnedOutboundTransferReferencesLocked(this, previousState, nextState); } void IDomainTransport.EnqueuePublishedPayloadChunks(long sender, string requestedHash, string baseHash, long requestId) { EnqueuePublishedPayloadChunksLocked(this, sender, requestedHash, baseHash, requestId); } void IDomainTransport.ReceivePayloadChunk(long sender, string hash, long requestId, int transferKind, string baseHash, int chunkIndex, int chunkCount, int compressedSize, byte[] chunkBytes) { ReceivePayloadChunkLocked(this, sender, hash, requestId, transferKind, baseHash, chunkIndex, chunkCount, compressedSize, chunkBytes); } bool IDomainTransport.TryTrimPublishedCacheOnce() { return TryTrimTransportCacheOnceLocked(this); } void IDomainTransport.ClearPinnedCacheReferences() { ClearPinnedCacheReferencesLocked(this); } void IDomainTransport.ResetState() { ResetTransportState(this); } } private readonly struct PreparedPublishPayload { public string Signature { get; } public string PayloadHash { get; } public byte[] PayloadBytes { get; } public int EntryCount { get; } public PreparedPublishPayload(string signature, string payloadHash, byte[] payloadBytes, int entryCount) { Signature = signature ?? ""; PayloadHash = payloadHash ?? ""; PayloadBytes = payloadBytes ?? Array.Empty(); EntryCount = Math.Max(0, entryCount); } } private readonly struct OutboundTransferKey : IEquatable { public long Sender { get; } public string DomainKey { get; } public OutboundTransferKey(long sender, string domainKey) { Sender = sender; DomainKey = domainKey ?? ""; } public bool Equals(OutboundTransferKey other) { if (Sender == other.Sender) { return string.Equals(DomainKey, other.DomainKey, StringComparison.Ordinal); } return false; } public override bool Equals(object? obj) { if (obj is OutboundTransferKey other) { return Equals(other); } return false; } public override int GetHashCode() { return (Sender.GetHashCode() * 397) ^ StringComparer.Ordinal.GetHashCode(DomainKey); } } private sealed class DomainCodec { public EntryTransportSchema Schema { get; } public Func, string> SignatureBuilder { get; } public Func EntrySignatureBuilder { get; } public Func, byte[]> Serializer { get; } public Func> Deserializer { get; } public Func?, List> CloneEntries { get; } public DomainCodec(EntryTransportSchema schema) { Schema = schema ?? throw new ArgumentNullException("schema"); SignatureBuilder = Schema.ComputePayloadSignature; EntrySignatureBuilder = (TEntry entry) => Schema.ComputeEntrySignature(entry, includeRuleId: false, includeResolvedBiomeMask: true); Serializer = Schema.SerializeEntries; Deserializer = Schema.DeserializeEntries; CloneEntries = Schema.CloneEntries; } } private readonly struct EntrySignatureContext { public bool IncludeRuleId { get; } public bool IncludeResolvedBiomeMask { get; } public EntrySignatureContext(bool includeRuleId, bool includeResolvedBiomeMask) { IncludeRuleId = includeRuleId; IncludeResolvedBiomeMask = includeResolvedBiomeMask; } } private sealed class EntryFieldSpec { public Action? SignatureWriter { get; } public Action? PayloadWriter { get; } public Action? PayloadReader { get; } public Action? CopyValue { get; } public EntryFieldSpec(Action? signatureWriter, Action? payloadWriter, Action? payloadReader, Action? copyValue) { SignatureWriter = signatureWriter; PayloadWriter = payloadWriter; PayloadReader = payloadReader; CopyValue = copyValue; } } private sealed class EntryTransportSchema { private readonly EntryFieldSpec[] _fields; public int DtoVersion { get; } public Func CreateEntry { get; } public EntryTransportSchema(int dtoVersion, Func createEntry, params EntryFieldSpec[] fields) { DtoVersion = dtoVersion; CreateEntry = createEntry ?? throw new ArgumentNullException("createEntry"); _fields = fields ?? Array.Empty>(); } public string ComputePayloadSignature(IReadOnlyList entries) { PayloadSignatureBuilder payloadSignatureBuilder = new PayloadSignatureBuilder(); payloadSignatureBuilder.WriteInt(DtoVersion); payloadSignatureBuilder.WriteInt(entries.Count); EntrySignatureContext context = new EntrySignatureContext(includeRuleId: true, includeResolvedBiomeMask: true); foreach (TEntry entry in entries) { WriteSignatureFields(payloadSignatureBuilder, entry, context); } return payloadSignatureBuilder.ComputeHash(); } public string ComputePayloadSignature(IReadOnlyList entries, Func selector) { if (selector == null) { throw new ArgumentNullException("selector"); } PayloadSignatureBuilder payloadSignatureBuilder = new PayloadSignatureBuilder(); payloadSignatureBuilder.WriteInt(DtoVersion); payloadSignatureBuilder.WriteInt(entries.Count); EntrySignatureContext context = new EntrySignatureContext(includeRuleId: true, includeResolvedBiomeMask: true); foreach (TSource entry in entries) { WriteSignatureFields(payloadSignatureBuilder, selector(entry), context); } return payloadSignatureBuilder.ComputeHash(); } public string ComputeEntrySignature(TEntry entry, bool includeRuleId, bool includeResolvedBiomeMask) { PayloadSignatureBuilder payloadSignatureBuilder = new PayloadSignatureBuilder(); WriteSignatureFields(payloadSignatureBuilder, entry, new EntrySignatureContext(includeRuleId, includeResolvedBiomeMask)); return payloadSignatureBuilder.ComputeHash(); } public byte[] SerializeEntries(List entries) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Expected O, but got Unknown ZPackage val = new ZPackage(); val.Write(DtoVersion); val.Write(entries.Count); foreach (TEntry entry in entries) { WritePayloadFields(val, entry); } return val.GetArray(); } public List DeserializeEntries(byte[] payloadBytes) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Expected O, but got Unknown ZPackage val = new ZPackage(payloadBytes); int num = val.ReadInt(); if (num != DtoVersion) { throw new InvalidDataException($"Unsupported DTO version '{num}'."); } int num2 = val.ReadInt(); List list = new List(num2); for (int i = 0; i < num2; i++) { TEntry val2 = CreateEntry(); ReadPayloadFields(val, val2); list.Add(val2); } return list; } public List CloneEntries(List? entries) { if (entries == null || entries.Count == 0) { return new List(); } List list = new List(entries.Count); foreach (TEntry entry in entries) { list.Add(CloneEntry(entry)); } return list; } private TEntry CloneEntry(TEntry source) { TEntry val = CreateEntry(); EntryFieldSpec[] fields = _fields; for (int i = 0; i < fields.Length; i++) { fields[i].CopyValue?.Invoke(source, val); } return val; } private void WriteSignatureFields(PayloadSignatureBuilder builder, TEntry entry, EntrySignatureContext context) { EntryFieldSpec[] fields = _fields; for (int i = 0; i < fields.Length; i++) { fields[i].SignatureWriter?.Invoke(builder, entry, context); } } private void WritePayloadFields(ZPackage package, TEntry entry) { EntryFieldSpec[] fields = _fields; for (int i = 0; i < fields.Length; i++) { fields[i].PayloadWriter?.Invoke(package, entry); } } private void ReadPayloadFields(ZPackage package, TEntry entry) { EntryFieldSpec[] fields = _fields; for (int i = 0; i < fields.Length; i++) { fields[i].PayloadReader?.Invoke(package, entry); } } } private sealed class PendingInboundTransfer { public long ExpectedSender { get; set; } public long RequestId { get; set; } public string Hash { get; set; } = ""; public bool IsFullFallbackRequest { get; set; } public string RequestedBaseHash { get; set; } = ""; public float LastProgressAt { get; set; } public int TransferKind { get; set; } = -1; public string BaseHash { get; set; } = ""; public int ChunkSizeBytes { get; set; } public int ChunkCount { get; set; } public int ExpectedCompressedSize { get; set; } = -1; public byte[] CompressedBuffer { get; set; } = Array.Empty(); public bool[] ReceivedChunks { get; set; } = Array.Empty(); public int ReceivedChunkCount { get; set; } } private sealed class PendingReloadAction { public int RoleEpoch { get; set; } public string DomainKey { get; set; } = ""; public Action Action { get; set; } } private sealed class PendingPayloadProcessingJob { public int RoleEpoch { get; set; } public Action Action { get; set; } } private sealed class PendingMainThreadPayloadCommit { public int RoleEpoch { get; set; } public Action Action { get; set; } } private sealed class PayloadEntryIndex { public List OrderedKeys { get; } = new List(); public Dictionary EntriesByKey { get; } = new Dictionary(StringComparer.OrdinalIgnoreCase); public Dictionary EntrySignaturesByKey { get; } = new Dictionary(StringComparer.OrdinalIgnoreCase); } private sealed class PublishedPayloadHistoryEntry { public string Hash { get; set; } = ""; public byte[] PayloadBytes { get; set; } = Array.Empty(); public PayloadEntryIndex? PayloadIndex { get; set; } public int EstimatedBytes { get; set; } public LinkedListNode? LruNode { get; set; } } private sealed class TransferArtifact { public string CacheKey { get; set; } = ""; public int TransferKind { get; set; } public string BaseHash { get; set; } = ""; public string TargetHash { get; set; } = ""; public byte[] CompressedBytes { get; set; } = Array.Empty(); public int ChunkSizeBytes { get; set; } public int ChunkCount { get; set; } public int EstimatedBytes { get; set; } public LinkedListNode? LruNode { get; set; } } private sealed class OutboundChunkMessage { public long Sender { get; set; } public string DomainKey { get; set; } = ""; public string Hash { get; set; } = ""; public long RequestId { get; set; } public int TransferKind { get; set; } public string BaseHash { get; set; } = ""; public int ChunkIndex { get; set; } public int ChunkCount { get; set; } public int CompressedSize { get; set; } } private sealed class OutboundTransferState { public int RoleEpoch { get; set; } public string Hash { get; set; } = ""; public long RequestId { get; set; } public TransferArtifact Artifact { get; set; } public int NextChunkIndex { get; set; } public bool IsQueued { get; set; } public float LastRequestedAt { get; set; } } private sealed class PreparedOutboundChunk { public OutboundTransferKey Key { get; set; } public int RoleEpoch { get; set; } public long RequestId { get; set; } public OutboundChunkMessage Message { get; set; } public byte[] SourceBytes { get; set; } = Array.Empty(); public int ChunkOffset { get; set; } public int ChunkLength { get; set; } } private sealed class ValueCodec where TValue : class { public Action SignatureWriter { get; } public Action PayloadWriter { get; } public Func PayloadReader { get; } public Func CloneValue { get; } public ValueCodec(Action signatureWriter, Action payloadWriter, Func payloadReader, Func cloneValue) { SignatureWriter = signatureWriter ?? throw new ArgumentNullException("signatureWriter"); PayloadWriter = payloadWriter ?? throw new ArgumentNullException("payloadWriter"); PayloadReader = payloadReader ?? throw new ArgumentNullException("payloadReader"); CloneValue = cloneValue ?? throw new ArgumentNullException("cloneValue"); } } private const string PayloadRequestRpc = "DropNSpawn Payload Request"; private const string PayloadChunkRpc = "DropNSpawn Payload Chunk"; private const int DefaultChunkSizeBytes = 240000; private const int MaxConcurrentSmallClientRequests = 2; private const int MaxConcurrentLargeClientRequests = 1; private const int MaxPayloadProcessingWorkers = 3; private const int MaxArtifactPrewarmWorkers = 1; private const int Sha256HexLength = 64; private const int MaxCompressedPayloadBytes = 67108864; private const int MaxUncompressedPayloadBytes = 268435456; private const int DecompressionBufferSizeBytes = 81920; private const float DefaultMaxDeltaChangedEntryRatio = 0.5f; private const float DefaultMaxDeltaCompressedSizeRatio = 0.85f; private const float RequestRetrySeconds = 5f; private const int TotalPublishedCacheBudgetBytes = 67108864; private const int PublishedCacheTrimLowWatermarkPercent = 85; private const int DeltaDtoVersion = 1; private const int FullTransferKind = 0; private const int DeltaTransferKind = 1; private static readonly object Sync = new object(); private static readonly FieldInfo? ZPackageWriterField = typeof(ZPackage).GetField("m_writer", BindingFlags.Instance | BindingFlags.NonPublic); private static readonly Dictionary TransportsByDomainKey = ((IEnumerable)DomainRegistry.Transports).ToDictionary((Func)((DomainTransportMetadata metadata) => metadata.DomainKey), (Func)CreateTransport, (IEqualityComparer?)StringComparer.Ordinal); private static readonly IDomainTransport[] AllTransports = DomainRegistry.Transports.Select((DomainTransportMetadata metadata) => TransportsByDomainKey[metadata.DomainKey]).ToArray(); private static readonly IDomainTransport[] SmallLaneRequestPriorityTransports = (from transport in AllTransports where !transport.UsesLargeRequestLane orderby transport.ClientRequestPriority descending select transport).ToArray(); private static readonly IDomainTransport[] LargeLaneRequestPriorityTransports = (from transport in AllTransports where transport.UsesLargeRequestLane orderby transport.ClientRequestPriority descending select transport).ToArray(); private static MonoBehaviour? _host; private static ZRoutedRpc? _registeredRpc; private static readonly RingBufferQueue PendingOutboundTransferKeys = new RingBufferQueue(); private static readonly Dictionary ActiveOutboundTransfers = new Dictionary(); private static readonly RingBufferQueue PendingCriticalPayloadProcessingJobs = new RingBufferQueue(); private static readonly RingBufferQueue PendingDeltaArtifactPrewarmJobs = new RingBufferQueue(); private static readonly RingBufferQueue PendingCachePersistenceJobs = new RingBufferQueue(); private static readonly RingBufferQueue PendingMainThreadPayloadCommits = new RingBufferQueue(); private static readonly RingBufferQueue PendingReloadActions = new RingBufferQueue(); private static readonly HashSet PendingReloadDomainKeys = new HashSet(StringComparer.Ordinal); private static int _outboundBudgetFrame = int.MinValue; private static int _outboundBytesSentThisFrame; private static int _payloadProcessingWorkersRunning; private static int _networkRoleEpoch; private static readonly FieldRef RoutedRpcIdRef = AccessTools.FieldRefAccess("m_id"); private static DomainCodec _objectCodec; private static DomainCodec _characterCodec; private static DomainCodec _spawnerCodec; private static DomainCodec _spawnSystemCodec; private static DomainCodec _eventCodec; private static ValueCodec _conditionsValueCodec; private static ValueCodec _characterDropValueCodec; private static ValueCodec _dropTableValueCodec; private static ValueCodec _damageableDropTableValueCodec; private static ValueCodec _pickableValueCodec; private static ValueCodec _pickableItemValueCodec; private static ValueCodec _fishValueCodec; private static ValueCodec _destructibleValueCodec; private static ValueCodec _spawnAreaValueCodec; private static ValueCodec _creatureSpawnerValueCodec; private static ValueCodec _spawnSystemSpawnValueCodec; private static ValueCodec _eventConditionsValueCodec; private static ValueCodec _eventSpawnValueCodec; internal static int CurrentAuthorityEpoch => Volatile.Read(in _networkRoleEpoch); private static DomainCodec ObjectCodec => _objectCodec ?? (_objectCodec = new DomainCodec(CreateObjectEntrySchema())); private static DomainCodec CharacterCodec => _characterCodec ?? (_characterCodec = new DomainCodec(CreateCharacterEntrySchema())); private static DomainCodec SpawnerCodec => _spawnerCodec ?? (_spawnerCodec = new DomainCodec(CreateSpawnerEntrySchema())); private static DomainCodec SpawnSystemCodec => _spawnSystemCodec ?? (_spawnSystemCodec = new DomainCodec(CreateSpawnSystemEntrySchema())); private static DomainCodec EventCodec => _eventCodec ?? (_eventCodec = new DomainCodec(CreateEventEntrySchema())); private static ValueCodec ConditionsValueCodec => _conditionsValueCodec ?? (_conditionsValueCodec = new ValueCodec(delegate(PayloadSignatureBuilder builder, ConditionsDefinition definition, EntrySignatureContext context) { WriteConditionsDefinition(builder, definition, context.IncludeResolvedBiomeMask); }, WriteConditionsDefinition, ReadConditionsDefinition, (ConditionsDefinition value) => ConfigurationEntryCloneSupport.CloneConditions(value))); private static ValueCodec CharacterDropValueCodec => _characterDropValueCodec ?? (_characterDropValueCodec = new ValueCodec(delegate(PayloadSignatureBuilder builder, CharacterDropDefinition definition, EntrySignatureContext _) { WriteCharacterDropDefinition(builder, definition); }, WriteCharacterDropDefinition, ReadCharacterDropDefinition, (CharacterDropDefinition value) => ConfigurationEntryCloneSupport.CloneCharacterDropDefinition(value))); private static ValueCodec DropTableValueCodec => _dropTableValueCodec ?? (_dropTableValueCodec = new ValueCodec(delegate(PayloadSignatureBuilder builder, DropTableDefinition definition, EntrySignatureContext _) { WriteDropTableDefinition(builder, definition); }, WriteDropTableDefinition, ReadDropTableDefinition, (DropTableDefinition value) => ConfigurationEntryCloneSupport.CloneDropTableDefinition(value))); private static ValueCodec DamageableDropTableValueCodec => _damageableDropTableValueCodec ?? (_damageableDropTableValueCodec = new ValueCodec(delegate(PayloadSignatureBuilder builder, DamageableDropTableDefinition definition, EntrySignatureContext _) { WriteDamageableDropTableDefinition(builder, definition); }, WriteDamageableDropTableDefinition, ReadDamageableDropTableDefinition, (DamageableDropTableDefinition value) => ConfigurationEntryCloneSupport.CloneDamageableDropTableDefinition(value))); private static ValueCodec PickableValueCodec => _pickableValueCodec ?? (_pickableValueCodec = new ValueCodec(delegate(PayloadSignatureBuilder builder, PickableDefinition definition, EntrySignatureContext _) { WritePickableDefinition(builder, definition); }, WritePickableDefinition, ReadPickableDefinition, (PickableDefinition value) => ConfigurationEntryCloneSupport.ClonePickableDefinition(value))); private static ValueCodec PickableItemValueCodec => _pickableItemValueCodec ?? (_pickableItemValueCodec = new ValueCodec(delegate(PayloadSignatureBuilder builder, PickableItemDefinition definition, EntrySignatureContext _) { WritePickableItemDefinition(builder, definition); }, WritePickableItemDefinition, ReadPickableItemDefinition, (PickableItemDefinition value) => ConfigurationEntryCloneSupport.ClonePickableItemDefinition(value))); private static ValueCodec FishValueCodec => _fishValueCodec ?? (_fishValueCodec = new ValueCodec(delegate(PayloadSignatureBuilder builder, FishDefinition definition, EntrySignatureContext _) { WriteFishDefinition(builder, definition); }, WriteFishDefinition, ReadFishDefinition, (FishDefinition value) => ConfigurationEntryCloneSupport.CloneFishDefinition(value))); private static ValueCodec DestructibleValueCodec => _destructibleValueCodec ?? (_destructibleValueCodec = new ValueCodec(delegate(PayloadSignatureBuilder builder, DestructibleDefinition definition, EntrySignatureContext _) { WriteDestructibleDefinition(builder, definition); }, WriteDestructibleDefinition, ReadDestructibleDefinition, (DestructibleDefinition value) => ConfigurationEntryCloneSupport.CloneDestructibleDefinition(value))); private static ValueCodec SpawnAreaValueCodec => _spawnAreaValueCodec ?? (_spawnAreaValueCodec = new ValueCodec(delegate(PayloadSignatureBuilder builder, SpawnAreaDefinition definition, EntrySignatureContext _) { WriteSpawnerSyncSpawnAreaDefinition(builder, definition); }, WriteSpawnerSyncSpawnAreaDefinition, ReadSpawnerSyncSpawnAreaDefinition, (SpawnAreaDefinition value) => ConfigurationEntryCloneSupport.CloneSpawnAreaDefinition(value))); private static ValueCodec CreatureSpawnerValueCodec => _creatureSpawnerValueCodec ?? (_creatureSpawnerValueCodec = new ValueCodec(delegate(PayloadSignatureBuilder builder, CreatureSpawnerDefinition definition, EntrySignatureContext _) { WriteSpawnerSyncCreatureSpawnerDefinition(builder, definition); }, WriteSpawnerSyncCreatureSpawnerDefinition, ReadSpawnerSyncCreatureSpawnerDefinition, (CreatureSpawnerDefinition value) => ConfigurationEntryCloneSupport.CloneCreatureSpawnerDefinition(value))); private static ValueCodec SpawnSystemSpawnValueCodec => _spawnSystemSpawnValueCodec ?? (_spawnSystemSpawnValueCodec = new ValueCodec(delegate(PayloadSignatureBuilder builder, SpawnSystemSpawnDefinition definition, EntrySignatureContext context) { WriteSpawnSystemSpawnDefinition(builder, definition, context.IncludeResolvedBiomeMask); }, WriteSpawnSystemSpawnDefinition, ReadSpawnSystemSpawnDefinition, (SpawnSystemSpawnDefinition value) => ConfigurationEntryCloneSupport.CloneSpawnSystemSpawnDefinition(value))); private static ValueCodec EventConditionsValueCodec => _eventConditionsValueCodec ?? (_eventConditionsValueCodec = new ValueCodec(delegate(PayloadSignatureBuilder builder, EventConditionsDefinition definition, EntrySignatureContext _) { WriteEventConditionsDefinition(builder, definition); }, WriteEventConditionsDefinition, ReadEventConditionsDefinition, (EventConditionsDefinition value) => ConfigurationEntryCloneSupport.CloneEventConditionsDefinition(value))); private static ValueCodec EventSpawnValueCodec => _eventSpawnValueCodec ?? (_eventSpawnValueCodec = new ValueCodec(delegate(PayloadSignatureBuilder builder, EventSpawnDefinition definition, EntrySignatureContext context) { WriteEventSpawnDefinition(builder, definition, context.IncludeResolvedBiomeMask); }, WriteEventSpawnDefinition, ReadEventSpawnDefinition, (EventSpawnDefinition value) => ConfigurationEntryCloneSupport.CloneEventSpawnDefinition(value))); private static DomainTransportPolicy ResolveTransportPolicy(DomainTransportProfile profile) { return profile switch { DomainTransportProfile.SmallConfig => new DomainTransportPolicy(8388608, usesLargeRequestLane: false, enableArtifactPrewarm: false), DomainTransportProfile.MediumConfig => new DomainTransportPolicy(12582912, usesLargeRequestLane: false, enableArtifactPrewarm: false), DomainTransportProfile.LargeConfig => new DomainTransportPolicy(25165824, usesLargeRequestLane: true, enableArtifactPrewarm: false), DomainTransportProfile.LargeWithArtifacts => new DomainTransportPolicy(25165824, usesLargeRequestLane: true, enableArtifactPrewarm: true), _ => new DomainTransportPolicy(8388608, usesLargeRequestLane: false, enableArtifactPrewarm: false), }; } private static IDomainTransport CreateTransport(DomainTransportMetadata metadata) { if (!(metadata is DomainTransportMetadata metadata2)) { if (!(metadata is DomainTransportMetadata metadata3)) { if (!(metadata is DomainTransportMetadata metadata4)) { if (!(metadata is DomainTransportMetadata metadata5)) { if (metadata is DomainTransportMetadata metadata6) { return new DomainTransport(metadata6, EventCodec); } throw new InvalidOperationException("Unsupported transport metadata for domain '" + metadata.DomainKey + "'."); } return new DomainTransport(metadata5, SpawnSystemCodec); } return new DomainTransport(metadata4, SpawnerCodec); } return new DomainTransport(metadata3, CharacterCodec); } return new DomainTransport(metadata2, ObjectCodec); } internal static void Initialize(MonoBehaviour host) { lock (Sync) { _host = host; EnsureRpcRegisteredLocked(); } } internal static void Shutdown() { lock (Sync) { _host = null; _registeredRpc = null; AdvanceRoleEpochAndResetWorkQueuesLocked(); foreach (IDomainTransport value in TransportsByDomainKey.Values) { value.ResetState(); } } } internal static bool HasPendingWork() { lock (Sync) { return PendingMainThreadPayloadCommits.Count > 0 || PendingReloadActions.Count > 0 || PendingOutboundTransferKeys.Count > 0 || HasPendingClientRequestWorkLocked(); } } internal static bool ProcessPendingWork(double deadline) { PreparedOutboundChunk preparedChunk = null; Action action = null; int num = -1; bool result = false; lock (Sync) { EnsureRpcRegisteredLocked(); int frameCount = Time.frameCount; if (_outboundBudgetFrame != frameCount) { _outboundBudgetFrame = frameCount; _outboundBytesSentThisFrame = 0; } PendingReloadAction pendingReload; if (TryDequeueCurrentMainThreadPayloadCommitLocked(out action)) { num = _networkRoleEpoch; } else if (TryDequeueCurrentReloadActionLocked(out pendingReload)) { action = pendingReload.Action; num = pendingReload.RoleEpoch; } else if (TryStartNextWaitingPayloadRequestLocked()) { result = true; } else if (ZRoutedRpc.instance != null && Time.realtimeSinceStartupAsDouble < deadline) { TryPrepareOutboundChunkForSendLocked(out preparedChunk); } } if (action != null) { lock (Sync) { if (num != _networkRoleEpoch) { return true; } } action(); return true; } if (preparedChunk != null) { bool flag; lock (Sync) { flag = preparedChunk.RoleEpoch == _networkRoleEpoch && DropNSpawnPlugin.IsSourceOfTruth; } bool flag2 = flag && SendOutboundChunk(preparedChunk); lock (Sync) { FinalizePreparedOutboundChunkLocked(preparedChunk, flag2); return flag2; } } return result; } internal static void HandleSourceOfTruthChanged(bool isSourceOfTruth) { lock (Sync) { EnsureRpcRegisteredLocked(); AdvanceRoleEpochAndResetWorkQueuesLocked(); foreach (IDomainTransport value in TransportsByDomainKey.Values) { value.ResetState(); } } } internal static bool IsPayloadCurrent(DomainDescriptor domain, string configurationSignature) { return IsPublishedPayloadCurrent(GetTransport(domain), configurationSignature); } internal static void PublishPayloadAsync(DomainDescriptor domain, List entries, string? knownSignature, Action applyManifest) { QueuePublishPayload(GetTransport(domain), entries, knownSignature, applyManifest); } internal static List CloneEntries(DomainDescriptor domain, List? entries) { return GetTransport(domain).Codec.CloneEntries(entries); } internal static bool TryGetEntries(DomainDescriptor domain, string manifestRaw, out List entries, out string payloadToken) { return TryGetEntries(GetTransport(domain), manifestRaw, out entries, out payloadToken); } internal static void HandleManifestChanged(DomainDescriptor domain, string manifestRaw) { HandleManifestChanged(GetTransport(domain), manifestRaw); } private static DomainTransport GetTransport(DomainDescriptor domain) { if (TransportsByDomainKey.TryGetValue(domain.DomainKey, out IDomainTransport value) && value is DomainTransport result) { return result; } throw new InvalidOperationException("No synchronized transport is registered for domain '" + domain.DomainKey + "'."); } private static void ResetTransportState(DomainTransport transport) { transport.LastPublishedSignature = ""; transport.LastPublishedManifest = ""; transport.PublishedPayloadBytes = null; transport.PublishedCompressedBytes = null; transport.PublishedPayloadIndex = null; transport.PublishedPayloadManifest = new PayloadManifest(); ClearPublishedPayloadHistoryLocked(transport); ClearPublishedTransferArtifactsLocked(transport); ClearPinnedCacheReferencesLocked(transport); transport.PendingArtifactBuildKeys.Clear(); transport.PublishVersion = 0; transport.PublishInFlight = false; transport.PendingPublishedSignature = ""; ResetClientState(transport); } private static void ResetClientState(DomainTransport transport) { transport.DesiredManifest = ""; transport.DesiredPayloadManifest = new PayloadManifest(); transport.InvalidDesiredManifest = ""; ClearBlockedManifestLocked(transport); transport.FullFallbackAttemptedHash = ""; transport.AvailableHash = ""; transport.AvailablePayloadBytes = null; transport.AvailableEntries = null; transport.AvailablePayloadIndex = null; transport.PendingTransfer = null; transport.NextRequestId = 0L; transport.RequestInFlight = false; transport.RequestStartedAt = 0f; transport.ProcessingInFlight = false; transport.ProcessingHash = ""; transport.ProcessingVersion++; } private static bool IsPublishedPayloadCurrent(DomainTransport transport, string configurationSignature) { lock (Sync) { return IsPublishedPayloadCurrentLocked(transport, configurationSignature); } } private static bool IsPublishedPayloadCurrentLocked(DomainTransport transport, string configurationSignature) { if (!string.Equals(transport.LastPublishedSignature, configurationSignature, StringComparison.Ordinal) || transport.PublishedPayloadBytes == null || transport.PublishedCompressedBytes == null || transport.LastPublishedManifest.Length <= 0) { if (transport.PublishInFlight && configurationSignature.Length > 0) { return string.Equals(transport.PendingPublishedSignature, configurationSignature, StringComparison.Ordinal); } return false; } return true; } private static PreparedPublishPayload PreparePublishPayloadLocked(DomainTransport transport, List? entries, string? knownSignature = null) { List list = entries ?? new List(); byte[] array = transport.Serializer(list); EnsurePayloadSizeWithinLimit(array.Length, transport.DisplayName); string payloadHash = ComputeSha256(array); return new PreparedPublishPayload((!string.IsNullOrWhiteSpace(knownSignature)) ? knownSignature : transport.SignatureBuilder(list), payloadHash, array, list.Count); } private static bool TryReusePublishedManifestForSignatureLocked(DomainTransport transport, string? signature, out string manifest) { manifest = ""; if (string.IsNullOrWhiteSpace(signature)) { return false; } if (!string.Equals(transport.LastPublishedSignature, signature, StringComparison.Ordinal) || transport.PublishedPayloadBytes == null || transport.PublishedCompressedBytes == null || transport.LastPublishedManifest.Length == 0) { return false; } manifest = transport.LastPublishedManifest; return true; } private static bool IsPublishPendingForSignatureLocked(DomainTransport transport, string? signature) { if (transport.PublishInFlight && !string.IsNullOrWhiteSpace(signature)) { return string.Equals(transport.PendingPublishedSignature, signature, StringComparison.Ordinal); } return false; } private static bool TryReusePublishedManifestForHashLocked(DomainTransport transport, string payloadHash, string payloadSignature, out string manifest) { manifest = ""; if (!string.Equals(transport.PublishedPayloadManifest.Hash, payloadHash, StringComparison.Ordinal) || transport.PublishedPayloadBytes == null || transport.PublishedCompressedBytes == null || transport.LastPublishedManifest.Length == 0) { return false; } transport.LastPublishedSignature = payloadSignature; manifest = transport.LastPublishedManifest; return true; } private static bool TryGetAvailableEntriesLocked(DomainTransport transport, out List entries) { if (transport.AvailablePayloadBytes == null) { entries = new List(); return false; } try { if (transport.AvailableEntries != null) { entries = transport.Codec.CloneEntries(transport.AvailableEntries); return true; } List list = (transport.AvailableEntries = transport.Deserializer(transport.AvailablePayloadBytes) ?? new List()); if (!string.IsNullOrWhiteSpace(transport.AvailableHash)) { BuildPayloadIndex(transport, list, transport.AvailableHash, out PayloadEntryIndex payloadIndex); transport.AvailablePayloadIndex = payloadIndex; } entries = transport.Codec.CloneEntries(list); return true; } catch (Exception ex) { string availableHash = transport.AvailableHash; entries = new List(); string reason = "source=lazy_deserialize error=" + ex.Message; InvalidateMalformedAvailablePayloadLocked(transport, availableHash, reason, ex); return false; } } private static void QueuePublishPayload(DomainTransport transport, List entries, string? knownSignature, Action applyManifest) { bool flag = false; string manifest = ""; int publishVersion = 0; int roleEpoch = 0; PreparedPublishPayload preparedPayload = default(PreparedPublishPayload); lock (Sync) { EnsureRpcRegisteredLocked(); if (TryReusePublishedManifestForSignatureLocked(transport, knownSignature, out manifest)) { flag = true; } else { if (IsPublishPendingForSignatureLocked(transport, knownSignature)) { return; } try { preparedPayload = PreparePublishPayloadLocked(transport, entries, knownSignature); } catch (Exception arg) { DropNSpawnPlugin.DropNSpawnLogger.LogError((object)$"Failed to capture synchronized {transport.DisplayName} payload publish snapshot. {arg}"); return; } if (TryReusePublishedManifestForSignatureLocked(transport, preparedPayload.Signature, out manifest)) { flag = true; } else { if (IsPublishPendingForSignatureLocked(transport, preparedPayload.Signature)) { return; } if (TryReusePublishedManifestForHashLocked(transport, preparedPayload.PayloadHash, preparedPayload.Signature, out manifest)) { flag = true; } else { publishVersion = ++transport.PublishVersion; roleEpoch = _networkRoleEpoch; transport.PublishInFlight = true; transport.PendingPublishedSignature = preparedPayload.Signature; string previousHashSnapshot = transport.PublishedPayloadManifest.Hash; byte[] previousPayloadBytesSnapshot = transport.PublishedPayloadBytes; PayloadEntryIndex previousPayloadIndexSnapshot = transport.PublishedPayloadIndex; QueueCriticalPayloadProcessingJobLocked(delegate { try { byte[] compressedPayload = CompressBytes(preparedPayload.PayloadBytes); PayloadEntryIndex preparedPublishedPayloadIndex = null; TransferArtifact primaryDeltaArtifact = null; if (previousHashSnapshot.Length > 0 && !string.Equals(previousHashSnapshot, preparedPayload.PayloadHash, StringComparison.Ordinal)) { TryBuildPrimaryDeltaArtifact(transport, previousHashSnapshot, previousPayloadBytesSnapshot, previousPayloadIndexSnapshot, preparedPayload.PayloadHash, preparedPayload.PayloadBytes, null, compressedPayload, out preparedPublishedPayloadIndex, out primaryDeltaArtifact); } PayloadManifest manifest2 = new PayloadManifest { Hash = preparedPayload.PayloadHash, CompressedSize = compressedPayload.Length, ChunkCount = Math.Max(1, (int)Math.Ceiling((double)compressedPayload.Length / (double)transport.ChunkSizeBytes)), EntryCount = preparedPayload.EntryCount }; QueueMainThreadPayloadCommitLocked(delegate { bool flag2 = false; string manifest3 = ""; lock (Sync) { if (roleEpoch != _networkRoleEpoch || publishVersion != transport.PublishVersion) { return; } transport.PublishInFlight = false; transport.PendingPublishedSignature = ""; if (TryReusePublishedManifestForHashLocked(transport, preparedPayload.PayloadHash, preparedPayload.Signature, out manifest3)) { flag2 = true; } else { string hash = transport.PublishedPayloadManifest.Hash; RememberPublishedPayloadLocked(transport, hash, transport.PublishedPayloadBytes, transport.PublishedPayloadIndex); transport.LastPublishedSignature = preparedPayload.Signature; transport.LastPublishedManifest = manifest2.Serialize(); transport.PublishedPayloadBytes = preparedPayload.PayloadBytes; transport.PublishedCompressedBytes = compressedPayload; transport.PublishedPayloadIndex = preparedPublishedPayloadIndex; transport.PublishedPayloadManifest = manifest2; ClearPublishedTransferArtifactsLocked(transport); EnsureFullTransferArtifactLocked(transport, preparedPayload.PayloadHash); bool flag3 = false; if (primaryDeltaArtifact != null && string.Equals(hash, previousHashSnapshot, StringComparison.Ordinal) && !transport.PublishedTransferArtifacts.ContainsKey(primaryDeltaArtifact.CacheKey)) { StoreTransferArtifactLocked(transport, primaryDeltaArtifact); flag3 = true; } TrimTransportCachesLocked(transport); TrimAllPublishedCachesLocked(); if (hash.Length > 0 && !string.Equals(hash, preparedPayload.PayloadHash, StringComparison.Ordinal) && !flag3 && !string.Equals(hash, previousHashSnapshot, StringComparison.Ordinal)) { QueueTransferArtifactBuildIfNeededLocked(transport, preparedPayload.PayloadHash, hash); } manifest3 = transport.LastPublishedManifest; flag2 = true; } } if (flag2) { applyManifest(manifest3); } }, roleEpoch); } catch (Exception ex) { Exception ex2 = ex; Exception ex3 = ex2; QueueMainThreadPayloadCommitLocked(delegate { lock (Sync) { if (roleEpoch != _networkRoleEpoch || publishVersion != transport.PublishVersion) { return; } transport.PublishInFlight = false; transport.PendingPublishedSignature = ""; } DropNSpawnPlugin.DropNSpawnLogger.LogError((object)$"Failed to prepare synchronized {transport.DisplayName} payload publish. {ex3}"); }, roleEpoch); } }, roleEpoch); } } } } if (flag) { applyManifest(manifest); } } private static bool TryGetEntries(DomainTransport transport, string? manifestRaw, out List entries, out string payloadToken) { lock (Sync) { EnsureRpcRegisteredLocked(); if (!TryParsePayloadManifest(transport, manifestRaw, out PayloadManifest manifest)) { bool shouldLog; bool result = TryPreserveLastKnownGoodOnInvalidManifestLocked(transport, manifestRaw, out entries, out payloadToken, out shouldLog); if (shouldLog) { DropNSpawnPlugin.DropNSpawnLogger.LogWarning((object)("Received invalid synchronized " + transport.DisplayName + " payload manifest '" + manifestRaw + "'. Keeping the last known good client payload state.")); } return result; } ClearInvalidManifestLocked(transport); transport.DesiredManifest = manifestRaw ?? ""; transport.DesiredPayloadManifest = manifest; RefreshBlockedManifestForDesiredHashLocked(transport, manifest.Hash); CancelProcessingIfHashMismatchLocked(transport, manifest.Hash); if (manifest.IsEmpty) { entries = new List(); payloadToken = ""; return false; } if (string.Equals(transport.AvailableHash, manifest.Hash, StringComparison.Ordinal) && transport.AvailablePayloadBytes != null && TryGetAvailableEntriesLocked(transport, out entries)) { payloadToken = manifest.Hash; return true; } if (IsManifestBlockedLocked(transport, manifest.Hash)) { entries = new List(); payloadToken = manifest.Hash; return false; } if (TryScheduleCachedPayloadLoadLocked(transport, manifest)) { entries = new List(); payloadToken = manifest.Hash; return false; } if (transport.ProcessingInFlight && string.Equals(transport.ProcessingHash, manifest.Hash, StringComparison.Ordinal)) { entries = new List(); payloadToken = manifest.Hash; return false; } EnsurePayloadRequestedLocked(transport, manifest); entries = new List(); payloadToken = manifest.Hash; return false; } } private static string NormalizeBaseHash(string? baseHash) { string text = (string.IsNullOrWhiteSpace(baseHash) ? "" : (baseHash ?? "").Trim()); if (!IsValidPayloadHash(text)) { return ""; } return text; } private static string BuildTransferArtifactCacheKey(string targetHash, string? baseHash) { return targetHash + "|" + NormalizeBaseHash(baseHash); } private static void AdvanceRoleEpochAndResetWorkQueuesLocked() { _networkRoleEpoch++; PendingOutboundTransferKeys.Clear(); ActiveOutboundTransfers.Clear(); IDomainTransport[] allTransports = AllTransports; for (int i = 0; i < allTransports.Length; i++) { allTransports[i].ClearPinnedCacheReferences(); } PendingCriticalPayloadProcessingJobs.Clear(); PendingDeltaArtifactPrewarmJobs.Clear(); PendingCachePersistenceJobs.Clear(); PendingMainThreadPayloadCommits.Clear(); PendingReloadActions.Clear(); PendingReloadDomainKeys.Clear(); _outboundBudgetFrame = int.MinValue; _outboundBytesSentThisFrame = 0; } private static bool TryReadCachedPayloadBytes(string cacheDirectoryName, string displayName, string hash, out byte[] payloadBytes, out byte[] compressedBytes) { string cachePath = GetCachePath(cacheDirectoryName, hash); payloadBytes = Array.Empty(); compressedBytes = Array.Empty(); if (!File.Exists(cachePath)) { return false; } long length = new FileInfo(cachePath).Length; if (length <= 0 || length > 67108864) { DropNSpawnPlugin.DropNSpawnLogger.LogWarning((object)("Discarding cached " + displayName + " payload '" + hash + "' because its compressed size " + length.ToString(CultureInfo.InvariantCulture) + " is outside the supported range.")); File.Delete(cachePath); return false; } compressedBytes = File.ReadAllBytes(cachePath); payloadBytes = DecompressBytes(compressedBytes); if (string.Equals(ComputeSha256(payloadBytes), hash, StringComparison.Ordinal)) { return true; } DropNSpawnPlugin.DropNSpawnLogger.LogWarning((object)("Discarding stale cached " + displayName + " payload '" + hash + "' because the cached hash does not match.")); File.Delete(cachePath); payloadBytes = Array.Empty(); compressedBytes = Array.Empty(); return false; } private static string GetCachePath(string cacheDirectoryName, string hash) { if (!IsValidPayloadHash(hash)) { throw new InvalidDataException("Payload cache hash must be a lowercase SHA-256 value."); } return Path.Combine(Path.Combine(DropNSpawnPlugin.YamlConfigDirectoryPath, "cache"), hash + "." + cacheDirectoryName + ".bin"); } private static void WriteCacheFile(string cacheDirectoryName, string hash, byte[] compressedBytes) { WriteCacheFile(cacheDirectoryName, hash, compressedBytes, (compressedBytes != null) ? compressedBytes.Length : 0); } private static void WriteCacheFile(string cacheDirectoryName, string hash, byte[] compressedBytes, int compressedLength) { string cachePath = GetCachePath(cacheDirectoryName, hash); string directoryName = Path.GetDirectoryName(cachePath); if (!string.IsNullOrWhiteSpace(directoryName)) { Directory.CreateDirectory(directoryName); } int num = Math.Max(0, Math.Min(compressedLength, (compressedBytes != null) ? compressedBytes.Length : 0)); if (num <= 0 || num > 67108864) { throw new InvalidDataException("Compressed payload size " + num.ToString(CultureInfo.InvariantCulture) + " is outside the supported range."); } using FileStream fileStream = File.Create(cachePath); fileStream.Write(compressedBytes, 0, num); } private static void DeleteCacheFileIfPresent(string cacheDirectoryName, string hash) { if (string.IsNullOrWhiteSpace(hash)) { return; } try { string cachePath = GetCachePath(cacheDirectoryName, hash); if (File.Exists(cachePath)) { File.Delete(cachePath); } } catch { } } private static string ComputeSha256(byte[] bytes) { using SHA256 sHA = SHA256.Create(); byte[] array = sHA.ComputeHash(bytes); StringBuilder stringBuilder = new StringBuilder(array.Length * 2); byte[] array2 = array; foreach (byte b in array2) { stringBuilder.Append(b.ToString("x2", CultureInfo.InvariantCulture)); } return stringBuilder.ToString(); } private static bool IsValidPayloadHash(string? hash) { if (hash == null || hash.Length != 64) { return false; } foreach (char c in hash) { if ((c < '0' || c > '9') && (c < 'a' || c > 'f')) { return false; } } return true; } private static bool TryParsePayloadManifest(DomainTransport transport, string? manifestRaw, out PayloadManifest manifest) { if (!PayloadManifest.TryParse(manifestRaw, out manifest)) { return false; } if (manifest.IsEmpty) { return string.IsNullOrWhiteSpace(manifestRaw); } int num = Math.Max(1, (int)Math.Ceiling((double)manifest.CompressedSize / (double)transport.ChunkSizeBytes)); return manifest.ChunkCount == num; } private static void EnsurePayloadSizeWithinLimit(int payloadSize, string displayName) { if (payloadSize < 0 || payloadSize > 268435456) { throw new InvalidDataException("Synchronized " + displayName + " payload size " + payloadSize.ToString(CultureInfo.InvariantCulture) + " exceeds the supported limit " + 268435456.ToString(CultureInfo.InvariantCulture) + "."); } } private static byte[] CompressBytes(byte[] input) { EnsurePayloadSizeWithinLimit(input.Length, "payload"); using MemoryStream memoryStream = new MemoryStream(); using (GZipStream gZipStream = new GZipStream(memoryStream, CompressionLevel.Fastest, leaveOpen: true)) { gZipStream.Write(input, 0, input.Length); } byte[] array = memoryStream.ToArray(); if (array.Length > 67108864) { throw new InvalidDataException("Compressed payload size " + array.Length.ToString(CultureInfo.InvariantCulture) + " exceeds the supported limit " + 67108864.ToString(CultureInfo.InvariantCulture) + "."); } return array; } private static byte[] DecompressBytes(byte[] input) { return DecompressBytes(input, (input != null) ? input.Length : 0, 0); } private static byte[] DecompressBytes(byte[] input, int inputLength, int initialCapacityHint) { if (input == null || inputLength <= 0) { return Array.Empty(); } int num = Math.Max(0, Math.Min(inputLength, input.Length)); if (num > 67108864) { throw new InvalidDataException("Compressed payload size " + num.ToString(CultureInfo.InvariantCulture) + " exceeds the supported limit " + 67108864.ToString(CultureInfo.InvariantCulture) + "."); } if (initialCapacityHint > 268435456) { throw new InvalidDataException("Payload capacity hint " + initialCapacityHint.ToString(CultureInfo.InvariantCulture) + " exceeds the supported limit " + 268435456.ToString(CultureInfo.InvariantCulture) + "."); } using MemoryStream stream = new MemoryStream(input, 0, num, writable: false, publiclyVisible: true); using GZipStream gZipStream = new GZipStream(stream, CompressionMode.Decompress); using MemoryStream memoryStream = ((initialCapacityHint > 0) ? new MemoryStream(initialCapacityHint) : new MemoryStream()); byte[] array = new byte[81920]; int num2; while ((num2 = gZipStream.Read(array, 0, array.Length)) > 0) { if (memoryStream.Length + num2 > 268435456) { throw new InvalidDataException("Decompressed payload exceeds the supported limit " + 268435456.ToString(CultureInfo.InvariantCulture) + "."); } memoryStream.Write(array, 0, num2); } return memoryStream.ToArray(); } private static void WriteCharacterDropDefinition(PayloadSignatureBuilder builder, CharacterDropDefinition definition) { WriteList(builder, definition.Drops, WriteCharacterDropEntryDefinition); } private static void WriteCharacterDropEntryDefinition(PayloadSignatureBuilder builder, CharacterDropEntryDefinition definition) { builder.WriteString(definition.Item ?? ""); WriteOptional(builder, definition.Amount, WriteIntRangeDefinition); WriteNullableInt(builder, definition.AmountMin); WriteNullableInt(builder, definition.AmountMax); WriteNullableFloat(builder, definition.Chance); WriteNullableBool(builder, definition.DontScale); WriteNullableBool(builder, definition.LevelMultiplier); WriteNullableBool(builder, definition.OnePerPlayer); WriteNullableInt(builder, definition.AmountLimit); WriteNullableBool(builder, definition.DropInStack); } private static void WriteDropTableDefinition(PayloadSignatureBuilder builder, DropTableDefinition definition) { WriteDropTablePayloadDefinition(builder, definition); } private static void WriteDamageableDropTableDefinition(PayloadSignatureBuilder builder, DamageableDropTableDefinition definition) { WriteNullableFloat(builder, definition.Health); WriteNullableInt(builder, definition.MinToolTier); WriteDropTablePayloadDefinition(builder, definition); } private static void WriteDropTablePayloadDefinition(PayloadSignatureBuilder builder, DropTablePayloadDefinition definition) { WriteOptional(builder, definition.Rolls, WriteIntRangeDefinition); WriteNullableInt(builder, definition.DropMin); WriteNullableInt(builder, definition.DropMax); WriteNullableFloat(builder, definition.DropChance); WriteNullableBool(builder, definition.OneOfEach); WriteList(builder, definition.Drops, WriteDropEntryDefinition); } private static void WriteDropEntryDefinition(PayloadSignatureBuilder builder, DropEntryDefinition definition) { builder.WriteString(definition.Item ?? ""); WriteOptional(builder, definition.Stack, WriteIntRangeDefinition); WriteNullableInt(builder, definition.StackMin); WriteNullableInt(builder, definition.StackMax); WriteNullableFloat(builder, definition.Weight); WriteNullableBool(builder, definition.DontScale); } private static void WriteSpawnerSyncSpawnAreaDefinition(PayloadSignatureBuilder builder, SpawnAreaDefinition definition) { WriteNullableFloat(builder, definition.LevelUpChance); WriteNullableFloat(builder, definition.SpawnInterval); WriteNullableFloat(builder, definition.TriggerDistance); WriteNullableBool(builder, definition.SetPatrolSpawnPoint); WriteNullableFloat(builder, definition.SpawnRadius); WriteNullableFloat(builder, definition.NearRadius); WriteNullableFloat(builder, definition.FarRadius); WriteNullableInt(builder, definition.MaxNear); WriteNullableInt(builder, definition.MaxTotal); WriteNullableInt(builder, definition.MaxTotalSpawns); WriteNullableBool(builder, definition.OnGroundOnly); WriteList(builder, definition.Creatures, WriteSpawnerSyncSpawnAreaCreatureDefinition); } private static void WriteSpawnerSyncSpawnAreaCreatureDefinition(PayloadSignatureBuilder builder, SpawnAreaSpawnDefinition definition) { builder.WriteString(definition.Creature ?? ""); WriteNullableFloat(builder, definition.Weight); WriteOptional(builder, definition.Level, WriteIntRangeDefinition); WriteNullableInt(builder, definition.MinLevel); WriteNullableInt(builder, definition.MaxLevel); WriteNullableString(builder, definition.Faction); WriteNullableString(builder, definition.Data); WriteStringDictionary(builder, definition.Fields); WriteStringList(builder, definition.Objects); } private static void WriteSpawnerSyncCreatureSpawnerDefinition(PayloadSignatureBuilder builder, CreatureSpawnerDefinition definition) { WriteNullableString(builder, definition.Creature); WriteOptional(builder, definition.TimeOfDay, WriteTimeOfDayDefinition); WriteNullableString(builder, definition.RequiredGlobalKey); WriteNullableString(builder, definition.BlockingGlobalKey); WriteOptional(builder, definition.Level, WriteIntRangeDefinition); WriteNullableInt(builder, definition.MinLevel); WriteNullableInt(builder, definition.MaxLevel); WriteNullableFloat(builder, definition.LevelUpChance); WriteNullableFloat(builder, definition.RespawnTimeMinutes); WriteNullableInt(builder, definition.SpawnCheckInterval); WriteNullableInt(builder, definition.SpawnGroupId); WriteNullableFloat(builder, definition.SpawnGroupRadius); WriteNullableFloat(builder, definition.SpawnerWeight); WriteNullableInt(builder, definition.MaxGroupSpawned); WriteNullableFloat(builder, definition.TriggerDistance); WriteNullableFloat(builder, definition.TriggerNoise); WriteNullableBool(builder, definition.RequireSpawnArea); WriteNullableBool(builder, definition.AllowInsidePlayerBase); WriteNullableBool(builder, definition.WakeUpAnimation); WriteNullableBool(builder, definition.SetPatrolSpawnPoint); WriteNullableString(builder, definition.Faction); WriteNullableString(builder, definition.Data); WriteStringDictionary(builder, definition.Fields); WriteStringList(builder, definition.Objects); } private static void WritePickableDefinition(PayloadSignatureBuilder builder, PickableDefinition definition) { WriteNullableString(builder, definition.OverrideName); WriteOptional(builder, definition.Drop, WritePickableDropDefinition); WriteOptional(builder, definition.ExtraDrops, WriteDropTablePayloadDefinition); } private static void WritePickableDropDefinition(PayloadSignatureBuilder builder, PickableDropDefinition definition) { builder.WriteString(definition.Item ?? ""); WriteNullableInt(builder, definition.Amount); WriteNullableInt(builder, definition.MinAmountScaled); WriteNullableBool(builder, definition.DontScale); } private static void WritePickableItemDefinition(PayloadSignatureBuilder builder, PickableItemDefinition definition) { WriteList(builder, definition.RandomDrops, WriteRandomPickableItemDefinition); WriteOptional(builder, definition.Drop, WritePickableItemDropDefinition); } private static void WriteFishDefinition(PayloadSignatureBuilder builder, FishDefinition definition) { WriteOptional(builder, definition.ExtraDrops, WriteDropTablePayloadDefinition); } private static void WriteDestructibleDefinition(PayloadSignatureBuilder builder, DestructibleDefinition definition) { WriteNullableFloat(builder, definition.Health); WriteNullableInt(builder, definition.MinToolTier); WriteNullableString(builder, definition.DestructibleType); WriteNullableString(builder, definition.SpawnWhenDestroyed); } private static void WritePickableItemDropDefinition(PayloadSignatureBuilder builder, PickableItemDropDefinition definition) { builder.WriteString(definition.Item ?? ""); WriteNullableInt(builder, definition.Stack); } private static void WriteRandomPickableItemDefinition(PayloadSignatureBuilder builder, RandomPickableItemDefinition definition) { builder.WriteString(definition.Item ?? ""); WriteOptional(builder, definition.Stack, WriteIntRangeDefinition); WriteNullableInt(builder, definition.StackMin); WriteNullableInt(builder, definition.StackMax); WriteNullableFloat(builder, definition.Weight); } private static void WriteConditionsDefinition(PayloadSignatureBuilder builder, ConditionsDefinition definition, bool includeResolvedBiomeMask) { WriteStringList(builder, definition.Biomes); if (includeResolvedBiomeMask) { WriteNullableInt(builder, GetResolvedBiomeMaskValue(definition.Biomes, definition.ResolvedBiomeMask)); } WriteStringList(builder, definition.Locations); WriteStringList(builder, definition.States); WriteStringList(builder, definition.Factions); WriteStringList(builder, definition.RequiredEnvironments); WriteStringList(builder, definition.RequiredGlobalKeys); WriteStringList(builder, definition.ForbiddenGlobalKeys); WriteOptional(builder, definition.Level, WriteIntRangeDefinition); WriteNullableInt(builder, definition.MinLevel); WriteNullableInt(builder, definition.MaxLevel); WriteOptional(builder, definition.Altitude, WriteFloatRangeDefinition); WriteNullableFloat(builder, definition.MinAltitude); WriteNullableFloat(builder, definition.MaxAltitude); WriteOptional(builder, definition.DistanceFromCenter, WriteFloatRangeDefinition); WriteNullableFloat(builder, definition.MinDistanceFromCenter); WriteNullableFloat(builder, definition.MaxDistanceFromCenter); WriteOptional(builder, definition.TimeOfDay, WriteTimeOfDayDefinition); WriteNullableBool(builder, definition.InDungeon); WriteNullableBool(builder, definition.InForest); WriteNullableBool(builder, definition.InsidePlayerBase); } private static void WriteIntRangeDefinition(PayloadSignatureBuilder builder, IntRangeDefinition definition) { WriteNullableInt(builder, definition.Min); WriteNullableInt(builder, definition.Max); } private static void WriteFloatRangeDefinition(PayloadSignatureBuilder builder, FloatRangeDefinition definition) { WriteNullableFloat(builder, definition.Min); WriteNullableFloat(builder, definition.Max); } private static void WriteTimeOfDayDefinition(PayloadSignatureBuilder builder, TimeOfDayDefinition definition) { WriteStringList(builder, definition.Values); } private static void WriteCharacterDropDefinition(ZPackage package, CharacterDropDefinition definition) { WriteList(package, definition.Drops, WriteCharacterDropEntryDefinition); } private static CharacterDropDefinition ReadCharacterDropDefinition(ZPackage package) { return new CharacterDropDefinition { Drops = ReadList(package, ReadCharacterDropEntryDefinition) }; } private static void WriteCharacterDropEntryDefinition(ZPackage package, CharacterDropEntryDefinition definition) { package.Write(definition.Item ?? ""); WriteOptional(package, definition.Amount, WriteIntRangeDefinition); WriteNullableInt(package, definition.AmountMin); WriteNullableInt(package, definition.AmountMax); WriteNullableFloat(package, definition.Chance); WriteNullableBool(package, definition.DontScale); WriteNullableBool(package, definition.LevelMultiplier); WriteNullableBool(package, definition.OnePerPlayer); WriteNullableInt(package, definition.AmountLimit); WriteNullableBool(package, definition.DropInStack); } private static CharacterDropEntryDefinition ReadCharacterDropEntryDefinition(ZPackage package) { return new CharacterDropEntryDefinition { Item = package.ReadString(), Amount = ReadOptional(package, ReadIntRangeDefinition), AmountMin = ReadNullableInt(package), AmountMax = ReadNullableInt(package), Chance = ReadNullableFloat(package), DontScale = ReadNullableBool(package), LevelMultiplier = ReadNullableBool(package), OnePerPlayer = ReadNullableBool(package), AmountLimit = ReadNullableInt(package), DropInStack = ReadNullableBool(package) }; } private static void WriteDropTableDefinition(ZPackage package, DropTableDefinition definition) { WriteDropTablePayloadDefinition(package, definition); } private static DropTableDefinition ReadDropTableDefinition(ZPackage package) { DropTablePayloadDefinition dropTablePayloadDefinition = ReadDropTablePayloadDefinition(package); return new DropTableDefinition { Rolls = dropTablePayloadDefinition.Rolls, DropMin = dropTablePayloadDefinition.DropMin, DropMax = dropTablePayloadDefinition.DropMax, DropChance = dropTablePayloadDefinition.DropChance, OneOfEach = dropTablePayloadDefinition.OneOfEach, Drops = dropTablePayloadDefinition.Drops }; } private static void WriteDamageableDropTableDefinition(ZPackage package, DamageableDropTableDefinition definition) { WriteNullableFloat(package, definition.Health); WriteNullableInt(package, definition.MinToolTier); WriteDropTablePayloadDefinition(package, definition); } private static DamageableDropTableDefinition ReadDamageableDropTableDefinition(ZPackage package) { float? health = ReadNullableFloat(package); int? minToolTier = ReadNullableInt(package); DropTablePayloadDefinition dropTablePayloadDefinition = ReadDropTablePayloadDefinition(package); return new DamageableDropTableDefinition { Health = health, MinToolTier = minToolTier, Rolls = dropTablePayloadDefinition.Rolls, DropMin = dropTablePayloadDefinition.DropMin, DropMax = dropTablePayloadDefinition.DropMax, DropChance = dropTablePayloadDefinition.DropChance, OneOfEach = dropTablePayloadDefinition.OneOfEach, Drops = dropTablePayloadDefinition.Drops }; } private static void WriteDropTablePayloadDefinition(ZPackage package, DropTablePayloadDefinition definition) { WriteOptional(package, definition.Rolls, WriteIntRangeDefinition); WriteNullableInt(package, definition.DropMin); WriteNullableInt(package, definition.DropMax); WriteNullableFloat(package, definition.DropChance); WriteNullableBool(package, definition.OneOfEach); WriteList(package, definition.Drops, WriteDropEntryDefinition); } private static DropTablePayloadDefinition ReadDropTablePayloadDefinition(ZPackage package) { return new DropTablePayloadDefinition { Rolls = ReadOptional(package, ReadIntRangeDefinition), DropMin = ReadNullableInt(package), DropMax = ReadNullableInt(package), DropChance = ReadNullableFloat(package), OneOfEach = ReadNullableBool(package), Drops = ReadList(package, ReadDropEntryDefinition) }; } private static void WriteDropEntryDefinition(ZPackage package, DropEntryDefinition definition) { package.Write(definition.Item ?? ""); WriteOptional(package, definition.Stack, WriteIntRangeDefinition); WriteNullableInt(package, definition.StackMin); WriteNullableInt(package, definition.StackMax); WriteNullableFloat(package, definition.Weight); WriteNullableBool(package, definition.DontScale); } private static DropEntryDefinition ReadDropEntryDefinition(ZPackage package) { return new DropEntryDefinition { Item = package.ReadString(), Stack = ReadOptional(package, ReadIntRangeDefinition), StackMin = ReadNullableInt(package), StackMax = ReadNullableInt(package), Weight = ReadNullableFloat(package), DontScale = ReadNullableBool(package) }; } private static void WriteSpawnerSyncSpawnAreaDefinition(ZPackage package, SpawnAreaDefinition definition) { WriteNullableFloat(package, definition.LevelUpChance); WriteNullableFloat(package, definition.SpawnInterval); WriteNullableFloat(package, definition.TriggerDistance); WriteNullableBool(package, definition.SetPatrolSpawnPoint); WriteNullableFloat(package, definition.SpawnRadius); WriteNullableFloat(package, definition.NearRadius); WriteNullableFloat(package, definition.FarRadius); WriteNullableInt(package, definition.MaxNear); WriteNullableInt(package, definition.MaxTotal); WriteNullableInt(package, definition.MaxTotalSpawns); WriteNullableBool(package, definition.OnGroundOnly); WriteList(package, definition.Creatures, WriteSpawnerSyncSpawnAreaCreatureDefinition); } private static SpawnAreaDefinition ReadSpawnerSyncSpawnAreaDefinition(ZPackage package) { return new SpawnAreaDefinition { LevelUpChance = ReadNullableFloat(package), SpawnInterval = ReadNullableFloat(package), TriggerDistance = ReadNullableFloat(package), SetPatrolSpawnPoint = ReadNullableBool(package), SpawnRadius = ReadNullableFloat(package), NearRadius = ReadNullableFloat(package), FarRadius = ReadNullableFloat(package), MaxNear = ReadNullableInt(package), MaxTotal = ReadNullableInt(package), MaxTotalSpawns = ReadNullableInt(package), OnGroundOnly = ReadNullableBool(package), Creatures = ReadList(package, ReadSpawnerSyncSpawnAreaCreatureDefinition) }; } private static void WriteSpawnerSyncSpawnAreaCreatureDefinition(ZPackage package, SpawnAreaSpawnDefinition definition) { package.Write(definition.Creature ?? ""); WriteNullableFloat(package, definition.Weight); WriteOptional(package, definition.Level, WriteIntRangeDefinition); WriteNullableInt(package, definition.MinLevel); WriteNullableInt(package, definition.MaxLevel); WriteNullableString(package, definition.Faction); WriteNullableString(package, definition.Data); WriteStringDictionary(package, definition.Fields); WriteStringList(package, definition.Objects); } private static SpawnAreaSpawnDefinition ReadSpawnerSyncSpawnAreaCreatureDefinition(ZPackage package) { return new SpawnAreaSpawnDefinition { Creature = package.ReadString(), Weight = ReadNullableFloat(package), Level = ReadOptional(package, ReadIntRangeDefinition), MinLevel = ReadNullableInt(package), MaxLevel = ReadNullableInt(package), Faction = ReadNullableString(package), Data = ReadNullableString(package), Fields = ReadStringDictionary(package), Objects = ReadStringList(package) }; } private static void WriteSpawnerSyncCreatureSpawnerDefinition(ZPackage package, CreatureSpawnerDefinition definition) { WriteNullableString(package, definition.Creature); WriteOptional(package, definition.TimeOfDay, WriteTimeOfDayDefinition); WriteNullableString(package, definition.RequiredGlobalKey); WriteNullableString(package, definition.BlockingGlobalKey); WriteOptional(package, definition.Level, WriteIntRangeDefinition); WriteNullableInt(package, definition.MinLevel); WriteNullableInt(package, definition.MaxLevel); WriteNullableFloat(package, definition.LevelUpChance); WriteNullableFloat(package, definition.RespawnTimeMinutes); WriteNullableInt(package, definition.SpawnCheckInterval); WriteNullableInt(package, definition.SpawnGroupId); WriteNullableFloat(package, definition.SpawnGroupRadius); WriteNullableFloat(package, definition.SpawnerWeight); WriteNullableInt(package, definition.MaxGroupSpawned); WriteNullableFloat(package, definition.TriggerDistance); WriteNullableFloat(package, definition.TriggerNoise); WriteNullableBool(package, definition.RequireSpawnArea); WriteNullableBool(package, definition.AllowInsidePlayerBase); WriteNullableBool(package, definition.WakeUpAnimation); WriteNullableBool(package, definition.SetPatrolSpawnPoint); WriteNullableString(package, definition.Faction); WriteNullableString(package, definition.Data); WriteStringDictionary(package, definition.Fields); WriteStringList(package, definition.Objects); } private static CreatureSpawnerDefinition ReadSpawnerSyncCreatureSpawnerDefinition(ZPackage package) { return new CreatureSpawnerDefinition { Creature = ReadNullableString(package), TimeOfDay = ReadOptional(package, ReadTimeOfDayDefinition), RequiredGlobalKey = ReadNullableString(package), BlockingGlobalKey = ReadNullableString(package), Level = ReadOptional(package, ReadIntRangeDefinition), MinLevel = ReadNullableInt(package), MaxLevel = ReadNullableInt(package), LevelUpChance = ReadNullableFloat(package), RespawnTimeMinutes = ReadNullableFloat(package), SpawnCheckInterval = ReadNullableInt(package), SpawnGroupId = ReadNullableInt(package), SpawnGroupRadius = ReadNullableFloat(package), SpawnerWeight = ReadNullableFloat(package), MaxGroupSpawned = ReadNullableInt(package), TriggerDistance = ReadNullableFloat(package), TriggerNoise = ReadNullableFloat(package), RequireSpawnArea = ReadNullableBool(package), AllowInsidePlayerBase = ReadNullableBool(package), WakeUpAnimation = ReadNullableBool(package), SetPatrolSpawnPoint = ReadNullableBool(package), Faction = ReadNullableString(package), Data = ReadNullableString(package), Fields = ReadStringDictionary(package), Objects = ReadStringList(package) }; } private static void WritePickableDefinition(ZPackage package, PickableDefinition definition) { WriteNullableString(package, definition.OverrideName); WriteOptional(package, definition.Drop, WritePickableDropDefinition); WriteOptional(package, definition.ExtraDrops, WriteDropTablePayloadDefinition); } private static PickableDefinition ReadPickableDefinition(ZPackage package) { return new PickableDefinition { OverrideName = ReadNullableString(package), Drop = ReadOptional(package, ReadPickableDropDefinition), ExtraDrops = ReadOptional(package, ReadDropTablePayloadDefinition) }; } private static void WritePickableDropDefinition(ZPackage package, PickableDropDefinition definition) { package.Write(definition.Item ?? ""); WriteNullableInt(package, definition.Amount); WriteNullableInt(package, definition.MinAmountScaled); WriteNullableBool(package, definition.DontScale); } private static PickableDropDefinition ReadPickableDropDefinition(ZPackage package) { return new PickableDropDefinition { Item = package.ReadString(), Amount = ReadNullableInt(package), MinAmountScaled = ReadNullableInt(package), DontScale = ReadNullableBool(package) }; } private static void WritePickableItemDefinition(ZPackage package, PickableItemDefinition definition) { WriteList(package, definition.RandomDrops, WriteRandomPickableItemDefinition); WriteOptional(package, definition.Drop, WritePickableItemDropDefinition); } private static void WriteFishDefinition(ZPackage package, FishDefinition definition) { WriteOptional(package, definition.ExtraDrops, WriteDropTablePayloadDefinition); } private static FishDefinition ReadFishDefinition(ZPackage package) { return new FishDefinition { ExtraDrops = ReadOptional(package, ReadDropTablePayloadDefinition) }; } private static PickableItemDefinition ReadPickableItemDefinition(ZPackage package) { return new PickableItemDefinition { RandomDrops = ReadList(package, ReadRandomPickableItemDefinition), Drop = ReadOptional(package, ReadPickableItemDropDefinition) }; } private static void WriteDestructibleDefinition(ZPackage package, DestructibleDefinition definition) { WriteNullableFloat(package, definition.Health); WriteNullableInt(package, definition.MinToolTier); WriteNullableString(package, definition.DestructibleType); WriteNullableString(package, definition.SpawnWhenDestroyed); } private static DestructibleDefinition ReadDestructibleDefinition(ZPackage package) { return new DestructibleDefinition { Health = ReadNullableFloat(package), MinToolTier = ReadNullableInt(package), DestructibleType = ReadNullableString(package), SpawnWhenDestroyed = ReadNullableString(package) }; } private static void WritePickableItemDropDefinition(ZPackage package, PickableItemDropDefinition definition) { package.Write(definition.Item ?? ""); WriteNullableInt(package, definition.Stack); } private static PickableItemDropDefinition ReadPickableItemDropDefinition(ZPackage package) { return new PickableItemDropDefinition { Item = package.ReadString(), Stack = ReadNullableInt(package) }; } private static void WriteRandomPickableItemDefinition(ZPackage package, RandomPickableItemDefinition definition) { package.Write(definition.Item ?? ""); WriteOptional(package, definition.Stack, WriteIntRangeDefinition); WriteNullableInt(package, definition.StackMin); WriteNullableInt(package, definition.StackMax); WriteNullableFloat(package, definition.Weight); } private static RandomPickableItemDefinition ReadRandomPickableItemDefinition(ZPackage package) { return new RandomPickableItemDefinition { Item = package.ReadString(), Stack = ReadOptional(package, ReadIntRangeDefinition), StackMin = ReadNullableInt(package), StackMax = ReadNullableInt(package), Weight = ReadNullableFloat(package) }; } private static void WriteConditionsDefinition(ZPackage package, ConditionsDefinition definition) { WriteStringList(package, definition.Biomes); WriteNullableInt(package, GetResolvedBiomeMaskValue(definition.Biomes, definition.ResolvedBiomeMask)); WriteStringList(package, definition.Locations); WriteStringList(package, definition.States); WriteStringList(package, definition.Factions); WriteStringList(package, definition.RequiredEnvironments); WriteStringList(package, definition.RequiredGlobalKeys); WriteStringList(package, definition.ForbiddenGlobalKeys); WriteOptional(package, definition.Level, WriteIntRangeDefinition); WriteNullableInt(package, definition.MinLevel); WriteNullableInt(package, definition.MaxLevel); WriteOptional(package, definition.Altitude, WriteFloatRangeDefinition); WriteNullableFloat(package, definition.MinAltitude); WriteNullableFloat(package, definition.MaxAltitude); WriteOptional(package, definition.DistanceFromCenter, WriteFloatRangeDefinition); WriteNullableFloat(package, definition.MinDistanceFromCenter); WriteNullableFloat(package, definition.MaxDistanceFromCenter); WriteOptional(package, definition.TimeOfDay, WriteTimeOfDayDefinition); WriteNullableBool(package, definition.InDungeon); WriteNullableBool(package, definition.InForest); WriteNullableBool(package, definition.InsidePlayerBase); } private static ConditionsDefinition ReadConditionsDefinition(ZPackage package) { ConditionsDefinition obj = new ConditionsDefinition { Biomes = ReadStringList(package) }; int? num = ReadNullableInt(package); Biome? resolvedBiomeMask; if (num.HasValue) { int valueOrDefault = num.GetValueOrDefault(); resolvedBiomeMask = (Biome)valueOrDefault; } else { resolvedBiomeMask = null; } obj.ResolvedBiomeMask = resolvedBiomeMask; obj.Locations = ReadStringList(package); obj.States = ReadStringList(package); obj.Factions = ReadStringList(package); obj.RequiredEnvironments = ReadStringList(package); obj.RequiredGlobalKeys = ReadStringList(package); obj.ForbiddenGlobalKeys = ReadStringList(package); obj.Level = ReadOptional(package, ReadIntRangeDefinition); obj.MinLevel = ReadNullableInt(package); obj.MaxLevel = ReadNullableInt(package); obj.Altitude = ReadOptional(package, ReadFloatRangeDefinition); obj.MinAltitude = ReadNullableFloat(package); obj.MaxAltitude = ReadNullableFloat(package); obj.DistanceFromCenter = ReadOptional(package, ReadFloatRangeDefinition); obj.MinDistanceFromCenter = ReadNullableFloat(package); obj.MaxDistanceFromCenter = ReadNullableFloat(package); obj.TimeOfDay = ReadOptional(package, ReadTimeOfDayDefinition); obj.InDungeon = ReadNullableBool(package); obj.InForest = ReadNullableBool(package); obj.InsidePlayerBase = ReadNullableBool(package); return obj; } private static int? GetResolvedBiomeMaskValue(IEnumerable? configuredBiomes, Biome? resolvedBiomeMask) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Expected I4, but got Unknown //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_003a: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Expected I4, but got Unknown if (resolvedBiomeMask.HasValue) { return (int)resolvedBiomeMask.Value; } Biome? val = BiomeResolutionSupport.ResolveBiomeMaskOrNull(configuredBiomes); if (val.HasValue) { Biome valueOrDefault = val.GetValueOrDefault(); return (int)valueOrDefault; } return null; } private static void WriteIntRangeDefinition(ZPackage package, IntRangeDefinition definition) { WriteNullableInt(package, definition.Min); WriteNullableInt(package, definition.Max); } private static IntRangeDefinition ReadIntRangeDefinition(ZPackage package) { return new IntRangeDefinition { Min = ReadNullableInt(package), Max = ReadNullableInt(package) }; } private static void WriteFloatRangeDefinition(ZPackage package, FloatRangeDefinition definition) { WriteNullableFloat(package, definition.Min); WriteNullableFloat(package, definition.Max); } private static FloatRangeDefinition ReadFloatRangeDefinition(ZPackage package) { return new FloatRangeDefinition { Min = ReadNullableFloat(package), Max = ReadNullableFloat(package) }; } private static void WriteTimeOfDayDefinition(ZPackage package, TimeOfDayDefinition definition) { WriteStringList(package, definition.Values); } private static TimeOfDayDefinition ReadTimeOfDayDefinition(ZPackage package) { return new TimeOfDayDefinition { Values = (ReadStringList(package) ?? new List()) }; } private static void WriteOptional(ZPackage package, T? value, Action writer) where T : class { bool flag = value != null; package.Write(flag); if (flag) { writer(package, value); } } private static T? ReadOptional(ZPackage package, Func reader) where T : class { if (!package.ReadBool()) { return null; } return reader(package); } private static void WriteOptional(PayloadSignatureBuilder builder, T? value, Action writer) where T : class { bool flag = value != null; builder.WriteBool(flag); if (flag) { writer(builder, value); } } private static void WriteList(ZPackage package, List? values, Action writer) { if (values == null) { package.Write(-1); return; } package.Write(values.Count); foreach (T value in values) { writer(package, value); } } private static void WriteList(PayloadSignatureBuilder builder, List? values, Action writer) { if (values == null) { builder.WriteInt(-1); return; } builder.WriteInt(values.Count); foreach (T value in values) { writer(builder, value); } } private static List? ReadList(ZPackage package, Func reader) { int num = package.ReadInt(); if (num < 0) { return null; } List list = new List(num); for (int i = 0; i < num; i++) { list.Add(reader(package)); } return list; } private static void WriteStringList(ZPackage package, List? values) { if (values == null) { package.Write(-1); return; } package.Write(values.Count); foreach (string value in values) { package.Write(value ?? ""); } } private static void WriteStringList(PayloadSignatureBuilder builder, List? values) { if (values == null) { builder.WriteInt(-1); return; } builder.WriteInt(values.Count); foreach (string value in values) { builder.WriteString(value ?? ""); } } private static List? ReadStringList(ZPackage package) { int num = package.ReadInt(); if (num < 0) { return null; } List list = new List(num); for (int i = 0; i < num; i++) { list.Add(package.ReadString()); } return list; } private static void WriteStringDictionary(ZPackage package, Dictionary? values) { if (values == null) { package.Write(-1); return; } List list = new List(values.Keys); list.Sort(StringComparer.Ordinal); package.Write(list.Count); foreach (string item in list) { string text = item ?? ""; package.Write(text); values.TryGetValue(text, out string value); WriteNullableString(package, value); } } private static void WriteStringDictionary(PayloadSignatureBuilder builder, Dictionary? values) { if (values == null) { builder.WriteInt(-1); return; } List list = new List(values.Keys); list.Sort(StringComparer.Ordinal); builder.WriteInt(list.Count); foreach (string item in list) { string text = item ?? ""; builder.WriteString(text); values.TryGetValue(text, out string value); WriteNullableString(builder, value); } } private static Dictionary? ReadStringDictionary(ZPackage package) { int num = package.ReadInt(); if (num < 0) { return null; } Dictionary dictionary = new Dictionary(num, StringComparer.OrdinalIgnoreCase); for (int i = 0; i < num; i++) { string key = package.ReadString(); string text = ReadNullableString(package); dictionary[key] = text ?? ""; } return dictionary; } private static void WriteNullableString(ZPackage package, string? value) { bool flag = value != null; package.Write(flag); if (flag) { package.Write(value); } } private static void WriteNullableString(PayloadSignatureBuilder builder, string? value) { bool flag = value != null; builder.WriteBool(flag); if (flag) { builder.WriteString(value); } } private static string? ReadNullableString(ZPackage package) { if (!package.ReadBool()) { return null; } return package.ReadString(); } private static void WriteNullableInt(ZPackage package, int? value) { package.Write(value.HasValue); if (value.HasValue) { package.Write(value.Value); } } private static void WriteNullableInt(PayloadSignatureBuilder builder, int? value) { builder.WriteBool(value.HasValue); if (value.HasValue) { builder.WriteInt(value.Value); } } private static int? ReadNullableInt(ZPackage package) { if (!package.ReadBool()) { return null; } return package.ReadInt(); } private static void WriteNullableFloat(ZPackage package, float? value) { package.Write(value.HasValue); if (value.HasValue) { package.Write(value.Value); } } private static void WriteNullableFloat(PayloadSignatureBuilder builder, float? value) { builder.WriteBool(value.HasValue); if (value.HasValue) { builder.WriteFloat(value.Value); } } private static float? ReadNullableFloat(ZPackage package) { if (!package.ReadBool()) { return null; } return package.ReadSingle(); } private static void WriteNullableBool(ZPackage package, bool? value) { package.Write(value.HasValue); if (value.HasValue) { package.Write(value.Value); } } private static void WriteNullableBool(PayloadSignatureBuilder builder, bool? value) { builder.WriteBool(value.HasValue); if (value.HasValue) { builder.WriteBool(value.Value); } } private static bool? ReadNullableBool(ZPackage package) { if (!package.ReadBool()) { return null; } return package.ReadBool(); } private static EntryFieldSpec StringField(Func getter, Action setter, Func? includeInSignature = null) { Func signatureFilter = includeInSignature ?? new Func(AlwaysIncludeInSignature); return new EntryFieldSpec(delegate(PayloadSignatureBuilder builder, TEntry entry, EntrySignatureContext context) { if (signatureFilter(context)) { builder.WriteString(getter(entry) ?? ""); } }, delegate(ZPackage package, TEntry entry) { package.Write(getter(entry) ?? ""); }, delegate(ZPackage package, TEntry entry) { setter(entry, package.ReadString()); }, delegate(TEntry source, TEntry target) { setter(target, getter(source) ?? ""); }); } private static EntryFieldSpec BoolField(Func getter, Action setter, Func? includeInSignature = null) { Func signatureFilter = includeInSignature ?? new Func(AlwaysIncludeInSignature); return new EntryFieldSpec(delegate(PayloadSignatureBuilder builder, TEntry entry, EntrySignatureContext context) { if (signatureFilter(context)) { builder.WriteBool(getter(entry)); } }, delegate(ZPackage package, TEntry entry) { package.Write(getter(entry)); }, delegate(ZPackage package, TEntry entry) { setter(entry, package.ReadBool()); }, delegate(TEntry source, TEntry target) { setter(target, getter(source)); }); } private static EntryFieldSpec NullableStringField(Func getter, Action setter, Func? includeInSignature = null) { Func signatureFilter = includeInSignature ?? new Func(AlwaysIncludeInSignature); return new EntryFieldSpec(delegate(PayloadSignatureBuilder builder, TEntry entry, EntrySignatureContext context) { if (signatureFilter(context)) { WriteNullableString(builder, getter(entry)); } }, delegate(ZPackage package, TEntry entry) { WriteNullableString(package, getter(entry)); }, delegate(ZPackage package, TEntry entry) { setter(entry, ReadNullableString(package)); }, delegate(TEntry source, TEntry target) { setter(target, getter(source)); }); } private static EntryFieldSpec NullableFloatField(Func getter, Action setter, Func? includeInSignature = null) { Func signatureFilter = includeInSignature ?? new Func(AlwaysIncludeInSignature); return new EntryFieldSpec(delegate(PayloadSignatureBuilder builder, TEntry entry, EntrySignatureContext context) { if (signatureFilter(context)) { WriteNullableFloat(builder, getter(entry)); } }, delegate(ZPackage package, TEntry entry) { WriteNullableFloat(package, getter(entry)); }, delegate(ZPackage package, TEntry entry) { setter(entry, ReadNullableFloat(package)); }, delegate(TEntry source, TEntry target) { setter(target, getter(source)); }); } private static EntryFieldSpec OptionalField(Func getter, Action setter, Action signatureWriter, Action payloadWriter, Func payloadReader, Func cloneValue, Func? includeInSignature = null) where TValue : class { Func signatureFilter = includeInSignature ?? new Func(AlwaysIncludeInSignature); return new EntryFieldSpec(delegate(PayloadSignatureBuilder builder, TEntry entry, EntrySignatureContext context) { if (signatureFilter(context)) { WriteOptional(builder, getter(entry), delegate(PayloadSignatureBuilder fieldBuilder, TValue value) { signatureWriter(fieldBuilder, value, context); }); } }, delegate(ZPackage package, TEntry entry) { WriteOptional(package, getter(entry), payloadWriter); }, delegate(ZPackage package, TEntry entry) { setter(entry, ReadOptional(package, payloadReader)); }, delegate(TEntry source, TEntry target) { setter(target, cloneValue(getter(source))); }); } private static EntryFieldSpec OptionalField(Func getter, Action setter, ValueCodec codec, Func? includeInSignature = null) where TValue : class { Func signatureFilter = includeInSignature ?? new Func(AlwaysIncludeInSignature); return new EntryFieldSpec(delegate(PayloadSignatureBuilder builder, TEntry entry, EntrySignatureContext context) { if (signatureFilter(context)) { WriteOptional(builder, getter(entry), delegate(PayloadSignatureBuilder fieldBuilder, TValue value) { codec.SignatureWriter(fieldBuilder, value, context); }); } }, delegate(ZPackage package, TEntry entry) { WriteOptional(package, getter(entry), codec.PayloadWriter); }, delegate(ZPackage package, TEntry entry) { setter(entry, ReadOptional(package, codec.PayloadReader)); }, delegate(TEntry source, TEntry target) { TValue val = getter(source); setter(target, (val == null) ? null : codec.CloneValue(val)); }); } private static EntryFieldSpec ListField(Func?> getter, Action?> setter, Action signatureWriter, Action payloadWriter, Func payloadReader, Func cloneValue, Func? includeInSignature = null) { Func signatureFilter = includeInSignature ?? new Func(AlwaysIncludeInSignature); return new EntryFieldSpec(delegate(PayloadSignatureBuilder builder, TEntry entry, EntrySignatureContext context) { if (signatureFilter(context)) { WriteList(builder, getter(entry), delegate(PayloadSignatureBuilder fieldBuilder, TValue value) { signatureWriter(fieldBuilder, value, context); }); } }, delegate(ZPackage package, TEntry entry) { WriteList(package, getter(entry), payloadWriter); }, delegate(ZPackage package, TEntry entry) { setter(entry, ReadList(package, payloadReader)); }, delegate(TEntry source, TEntry target) { setter(target, CloneListValue(getter(source), cloneValue)); }); } private static EntryFieldSpec ListField(Func?> getter, Action?> setter, ValueCodec codec, Func? includeInSignature = null) where TValue : class { Func signatureFilter = includeInSignature ?? new Func(AlwaysIncludeInSignature); return new EntryFieldSpec(delegate(PayloadSignatureBuilder builder, TEntry entry, EntrySignatureContext context) { if (signatureFilter(context)) { WriteList(builder, getter(entry), delegate(PayloadSignatureBuilder fieldBuilder, TValue value) { codec.SignatureWriter(fieldBuilder, value, context); }); } }, delegate(ZPackage package, TEntry entry) { WriteList(package, getter(entry), codec.PayloadWriter); }, delegate(ZPackage package, TEntry entry) { setter(entry, ReadList(package, codec.PayloadReader)); }, delegate(TEntry source, TEntry target) { setter(target, CloneListValue(getter(source), codec.CloneValue)); }); } private static EntryFieldSpec CopyOnlyField(Action copyValue) { return new EntryFieldSpec(null, null, null, copyValue); } private static bool AlwaysIncludeInSignature(EntrySignatureContext context) { return true; } private static List? CloneListValue(List? source, Func cloneValue) { if (source == null) { return null; } List list = new List(source.Count); foreach (TValue item in source) { list.Add(cloneValue(item)); } return list; } private static int GetInboundChunkOffset(int chunkSizeBytes, int chunkIndex) { return checked(chunkIndex * chunkSizeBytes); } private static bool TryGetInboundCompressedBufferCapacity(int chunkCount, int chunkIndex, int chunkLength, int compressedSize, int chunkSizeBytes, out int capacity, out string failureReason) { capacity = 0; failureReason = ""; if (compressedSize <= 0) { failureReason = $"compressed size {compressedSize} must be greater than 0."; return false; } if (compressedSize > 67108864) { failureReason = $"compressed size {compressedSize} exceeds the supported limit {67108864}."; return false; } if (chunkLength < 0 || chunkLength > chunkSizeBytes) { failureReason = $"chunk length {chunkLength} is outside the valid range 0-{chunkSizeBytes}."; return false; } int num = Math.Max(1, (int)Math.Ceiling((double)compressedSize / (double)chunkSizeBytes)); if (num != chunkCount) { failureReason = $"chunk count {chunkCount} did not match compressed size {compressedSize} descriptor chunk count {num}."; return false; } if (chunkIndex < chunkCount - 1 && chunkLength != chunkSizeBytes) { failureReason = $"non-terminal chunk {chunkIndex} had length {chunkLength} instead of {chunkSizeBytes}."; return false; } long num2 = (long)GetInboundChunkOffset(chunkSizeBytes, chunkIndex) + (long)chunkLength; long num3 = compressedSize; if (num3 < num2 || num3 > int.MaxValue) { failureReason = $"compressed buffer capacity {num3} is invalid for chunk {chunkIndex} length {chunkLength}."; return false; } capacity = (int)num3; return true; } private static bool TryInitializePendingInboundTransferBufferLocked(DomainTransport transport, PendingInboundTransfer pendingTransfer, string hash, int transferKind, string normalizedBaseHash, int chunkCount, int compressedSize, int chunkIndex, int chunkLength, out string failureReason) { failureReason = ""; pendingTransfer.TransferKind = transferKind; pendingTransfer.BaseHash = normalizedBaseHash; pendingTransfer.ChunkSizeBytes = transport.ChunkSizeBytes; pendingTransfer.ChunkCount = chunkCount; if (transferKind == 0 && string.Equals(transport.DesiredPayloadManifest.Hash, hash, StringComparison.Ordinal)) { PayloadManifest desiredPayloadManifest = transport.DesiredPayloadManifest; if (desiredPayloadManifest.ChunkCount > 0 && desiredPayloadManifest.ChunkCount != chunkCount) { failureReason = $"full transfer chunk count {chunkCount} did not match desired manifest chunk count {desiredPayloadManifest.ChunkCount}."; return false; } if (desiredPayloadManifest.CompressedSize > 0 && desiredPayloadManifest.CompressedSize != compressedSize) { failureReason = $"full transfer compressed size {compressedSize} did not match desired manifest compressed size {desiredPayloadManifest.CompressedSize}."; return false; } } if (!TryGetInboundCompressedBufferCapacity(chunkCount, chunkIndex, chunkLength, compressedSize, transport.ChunkSizeBytes, out var capacity, out failureReason)) { return false; } pendingTransfer.ExpectedCompressedSize = compressedSize; pendingTransfer.CompressedBuffer = new byte[capacity]; pendingTransfer.ReceivedChunks = new bool[chunkCount]; pendingTransfer.ReceivedChunkCount = 0; return true; } private static bool TryValidateInboundChunkBounds(PendingInboundTransfer pendingTransfer, int chunkIndex, int chunkLength, out string failureReason) { failureReason = ""; int num = Math.Max(1, pendingTransfer.ChunkSizeBytes); if (chunkLength < 0 || chunkLength > num) { failureReason = $"chunk length {chunkLength} is outside the valid range 0-{num}."; return false; } bool flag = chunkIndex == pendingTransfer.ChunkCount - 1; if (!flag && chunkLength != num) { failureReason = $"non-terminal chunk {chunkIndex} had length {chunkLength} instead of {num}."; return false; } int inboundChunkOffset = GetInboundChunkOffset(num, chunkIndex); if (inboundChunkOffset < 0 || inboundChunkOffset > pendingTransfer.CompressedBuffer.Length) { failureReason = $"chunk {chunkIndex} offset {inboundChunkOffset} exceeded compressed buffer length {pendingTransfer.CompressedBuffer.Length}."; return false; } int num2 = pendingTransfer.CompressedBuffer.Length - inboundChunkOffset; if (!flag) { if (chunkLength > num2) { failureReason = $"chunk {chunkIndex} length {chunkLength} exceeded remaining compressed buffer capacity {num2}."; return false; } return true; } int num3 = pendingTransfer.ExpectedCompressedSize - inboundChunkOffset; if (num3 <= 0 || num3 > num) { failureReason = $"last chunk {chunkIndex} expected length {num3} is invalid."; return false; } if (chunkLength != num3) { failureReason = $"last chunk {chunkIndex} length {chunkLength} did not match expected length {num3}."; return false; } return true; } private static bool BufferedChunkEquals(byte[] buffer, int offset, byte[] chunkBytes) { for (int i = 0; i < chunkBytes.Length; i++) { if (buffer[offset + i] != chunkBytes[i]) { return false; } } return true; } private static bool TryBufferInboundChunkBytes(PendingInboundTransfer pendingTransfer, int chunkIndex, byte[] chunkBytes, out bool isDuplicate, out string failureReason) { isDuplicate = false; if (!TryValidateInboundChunkBounds(pendingTransfer, chunkIndex, chunkBytes.Length, out failureReason)) { return false; } bool flag = chunkIndex == pendingTransfer.ChunkCount - 1; int inboundChunkOffset = GetInboundChunkOffset(Math.Max(1, pendingTransfer.ChunkSizeBytes), chunkIndex); if (pendingTransfer.ReceivedChunks[chunkIndex]) { int num = (flag ? (pendingTransfer.ExpectedCompressedSize - inboundChunkOffset) : pendingTransfer.ChunkSizeBytes); isDuplicate = true; if (num != chunkBytes.Length || !BufferedChunkEquals(pendingTransfer.CompressedBuffer, inboundChunkOffset, chunkBytes)) { failureReason = $"duplicate chunk {chunkIndex} differed from the already buffered data."; return false; } return true; } Buffer.BlockCopy(chunkBytes, 0, pendingTransfer.CompressedBuffer, inboundChunkOffset, chunkBytes.Length); pendingTransfer.ReceivedChunks[chunkIndex] = true; pendingTransfer.ReceivedChunkCount++; failureReason = ""; return true; } private static int GetInboundCompressedLength(PendingInboundTransfer pendingTransfer) { return Math.Max(0, pendingTransfer.ExpectedCompressedSize); } private static bool TryGetPublishedTransferArtifactLocked(DomainTransport transport, string targetHash, string? baseHash, out TransferArtifact? artifact) { artifact = null; string key = BuildTransferArtifactCacheKey(targetHash, baseHash); if (!transport.PublishedTransferArtifacts.TryGetValue(key, out artifact) || artifact == null) { return false; } LinkedListNode node = artifact.LruNode; TouchLruNodeLocked(transport.PublishedTransferArtifactLru, key, ref node); artifact.LruNode = node; return true; } private static void StoreTransferArtifactLocked(DomainTransport transport, TransferArtifact artifact) { if (artifact != null && artifact.CacheKey.Length != 0) { if (transport.PublishedTransferArtifacts.TryGetValue(artifact.CacheKey, out TransferArtifact value) && value != null) { RemoveTransferArtifactLocked(transport, value); } transport.PublishedTransferArtifacts[artifact.CacheKey] = artifact; AdjustPublishedTransferArtifactBytesLocked(transport, artifact.EstimatedBytes); LinkedListNode node = artifact.LruNode; TouchLruNodeLocked(transport.PublishedTransferArtifactLru, artifact.CacheKey, ref node); artifact.LruNode = node; } } private static TransferArtifact? EnsureFullTransferArtifactLocked(DomainTransport transport, string targetHash) { if (transport.PublishedCompressedBytes == null || transport.PublishedPayloadManifest.IsEmpty || !string.Equals(transport.PublishedPayloadManifest.Hash, targetHash, StringComparison.Ordinal)) { return null; } if (TryGetPublishedTransferArtifactLocked(transport, targetHash, "", out TransferArtifact artifact)) { return artifact; } TransferArtifact transferArtifact = new TransferArtifact { CacheKey = BuildTransferArtifactCacheKey(targetHash, ""), TransferKind = 0, BaseHash = "", TargetHash = targetHash, CompressedBytes = transport.PublishedCompressedBytes, ChunkSizeBytes = transport.ChunkSizeBytes, ChunkCount = Math.Max(1, (int)Math.Ceiling((double)transport.PublishedCompressedBytes.Length / (double)transport.ChunkSizeBytes)) }; transferArtifact.EstimatedBytes = EstimateTransferArtifactBytes(transferArtifact); StoreTransferArtifactLocked(transport, transferArtifact); return transferArtifact; } private static void QueueTransferArtifactBuildIfNeededLocked(DomainTransport transport, string targetHash, string? baseHash) { if (!transport.EnableArtifactPrewarm) { return; } string normalizedBaseHash = NormalizeBaseHash(baseHash); if (normalizedBaseHash.Length == 0 || transport.PublishedCompressedBytes == null || transport.PublishedPayloadManifest.IsEmpty || !string.Equals(transport.PublishedPayloadManifest.Hash, targetHash, StringComparison.Ordinal) || !transport.PublishedPayloadHistory.TryGetValue(normalizedBaseHash, out PublishedPayloadHistoryEntry value) || value == null || value.PayloadBytes == null || value.PayloadBytes.Length == 0 || TryGetPublishedTransferArtifactLocked(transport, targetHash, normalizedBaseHash, out TransferArtifact _)) { return; } string cacheKey = BuildTransferArtifactCacheKey(targetHash, normalizedBaseHash); if (!transport.PendingArtifactBuildKeys.Add(cacheKey)) { return; } byte[] fullCompressedPayloadBytes = transport.PublishedCompressedBytes; byte[] targetPayloadBytes = transport.PublishedPayloadBytes ?? Array.Empty(); PayloadEntryIndex targetPayloadIndex = transport.PublishedPayloadIndex; byte[] basePayloadBytes = value.PayloadBytes; PayloadEntryIndex basePayloadIndex = value.PayloadIndex; int roleEpoch = _networkRoleEpoch; LinkedListNode node = value.LruNode; TouchLruNodeLocked(transport.PublishedPayloadHistoryLru, normalizedBaseHash, ref node); value.LruNode = node; QueueDeltaArtifactPrewarmJobLocked(delegate { try { PayloadEntryIndex resolvedTargetPayloadIndex; PayloadEntryIndex resolvedBasePayloadIndex; byte[] deltaPayloadBytes; if (!IsCurrentRoleEpoch(roleEpoch)) { QueueMainThreadPayloadCommitLocked(delegate { lock (Sync) { transport.PendingArtifactBuildKeys.Remove(cacheKey); } }, roleEpoch); } else if (!TryEnsurePayloadIndex(transport, targetHash, targetPayloadBytes, targetPayloadIndex, out resolvedTargetPayloadIndex) || resolvedTargetPayloadIndex == null) { QueueMainThreadPayloadCommitLocked(delegate { lock (Sync) { transport.PendingArtifactBuildKeys.Remove(cacheKey); } }, roleEpoch); } else if (!IsCurrentRoleEpoch(roleEpoch)) { QueueMainThreadPayloadCommitLocked(delegate { lock (Sync) { transport.PendingArtifactBuildKeys.Remove(cacheKey); } }, roleEpoch); } else if (!TryEnsurePayloadIndex(transport, normalizedBaseHash, basePayloadBytes, basePayloadIndex, out resolvedBasePayloadIndex) || resolvedBasePayloadIndex == null) { QueueMainThreadPayloadCommitLocked(delegate { lock (Sync) { transport.PendingArtifactBuildKeys.Remove(cacheKey); } }, roleEpoch); } else if (!IsCurrentRoleEpoch(roleEpoch)) { QueueMainThreadPayloadCommitLocked(delegate { lock (Sync) { transport.PendingArtifactBuildKeys.Remove(cacheKey); } }, roleEpoch); } else if (!TryBuildDeltaPayloadBytes(transport, normalizedBaseHash, targetHash, resolvedBasePayloadIndex, resolvedTargetPayloadIndex, out deltaPayloadBytes)) { QueueMainThreadPayloadCommitLocked(delegate { lock (Sync) { transport.PendingArtifactBuildKeys.Remove(cacheKey); } }, roleEpoch); } else if (!IsCurrentRoleEpoch(roleEpoch)) { QueueMainThreadPayloadCommitLocked(delegate { lock (Sync) { transport.PendingArtifactBuildKeys.Remove(cacheKey); } }, roleEpoch); } else { byte[] array = CompressBytes(deltaPayloadBytes); if (!IsCurrentRoleEpoch(roleEpoch)) { QueueMainThreadPayloadCommitLocked(delegate { lock (Sync) { transport.PendingArtifactBuildKeys.Remove(cacheKey); } }, roleEpoch); } else if ((float)array.Length >= (float)fullCompressedPayloadBytes.Length * 0.85f) { QueueMainThreadPayloadCommitLocked(delegate { lock (Sync) { transport.PendingArtifactBuildKeys.Remove(cacheKey); } }, roleEpoch); } else { TransferArtifact artifact2 = new TransferArtifact { CacheKey = cacheKey, TransferKind = 1, BaseHash = normalizedBaseHash, TargetHash = targetHash, CompressedBytes = array, ChunkSizeBytes = transport.ChunkSizeBytes, ChunkCount = Math.Max(1, (int)Math.Ceiling((double)array.Length / (double)transport.ChunkSizeBytes)) }; artifact2.EstimatedBytes = EstimateTransferArtifactBytes(artifact2); QueueMainThreadPayloadCommitLocked(delegate { lock (Sync) { transport.PendingArtifactBuildKeys.Remove(cacheKey); if (roleEpoch == _networkRoleEpoch && !transport.PublishedPayloadManifest.IsEmpty && string.Equals(transport.PublishedPayloadManifest.Hash, targetHash, StringComparison.Ordinal) && !transport.PublishedTransferArtifacts.ContainsKey(cacheKey)) { if (transport.PublishedPayloadIndex == null) { transport.PublishedPayloadIndex = resolvedTargetPayloadIndex; } RememberPublishedPayloadLocked(transport, normalizedBaseHash, basePayloadBytes, resolvedBasePayloadIndex); StoreTransferArtifactLocked(transport, artifact2); TrimTransportCachesLocked(transport); TrimAllPublishedCachesLocked(); } } }, roleEpoch); } } } catch { QueueMainThreadPayloadCommitLocked(delegate { lock (Sync) { transport.PendingArtifactBuildKeys.Remove(cacheKey); } }, roleEpoch); } }, roleEpoch); } private static long AllocateRequestIdLocked(DomainTransport transport) { long num = transport.NextRequestId + 1; if (num <= 0) { num = 1L; } transport.NextRequestId = num; return num; } private static long BeginPendingInboundTransferLocked(DomainTransport transport, string hash, string requestedBaseHash, bool isFullFallbackRequest, long expectedSender, out string resolvedBaseHash) { float realtimeSinceStartup = Time.realtimeSinceStartup; PendingInboundTransfer pendingTransfer = transport.PendingTransfer; if (pendingTransfer != null && string.Equals(pendingTransfer.Hash, hash, StringComparison.Ordinal) && pendingTransfer.IsFullFallbackRequest == isFullFallbackRequest) { pendingTransfer.ExpectedSender = expectedSender; if (pendingTransfer.RequestId <= 0) { pendingTransfer.RequestId = AllocateRequestIdLocked(transport); } if (pendingTransfer.RequestedBaseHash.Length == 0) { pendingTransfer.RequestedBaseHash = ((pendingTransfer.TransferKind >= 0) ? pendingTransfer.BaseHash : NormalizeBaseHash(requestedBaseHash)); } transport.RequestInFlight = true; transport.RequestStartedAt = realtimeSinceStartup; resolvedBaseHash = pendingTransfer.RequestedBaseHash; return pendingTransfer.RequestId; } long num = AllocateRequestIdLocked(transport); transport.PendingTransfer = new PendingInboundTransfer { ExpectedSender = expectedSender, RequestId = num, Hash = (hash ?? ""), IsFullFallbackRequest = isFullFallbackRequest, RequestedBaseHash = NormalizeBaseHash(requestedBaseHash) }; transport.RequestInFlight = true; transport.RequestStartedAt = realtimeSinceStartup; resolvedBaseHash = transport.PendingTransfer.RequestedBaseHash; return num; } private static void ClearPendingInboundTransferLocked(DomainTransport transport) { transport.PendingTransfer = null; transport.RequestInFlight = false; transport.RequestStartedAt = 0f; } private static bool MatchesCurrentPendingRequestLocked(DomainTransport transport, string hash) { if (transport.PendingTransfer != null) { return string.Equals(transport.PendingTransfer.Hash, hash, StringComparison.Ordinal); } return false; } private static float GetPendingRequestLastActivityAtLocked(DomainTransport transport) { float num = transport.RequestStartedAt; PendingInboundTransfer? pendingTransfer = transport.PendingTransfer; if (pendingTransfer != null && pendingTransfer.LastProgressAt > num) { num = transport.PendingTransfer.LastProgressAt; } return num; } private static bool HasPendingRequestTimedOutLocked(DomainTransport transport) { if (!transport.RequestInFlight) { return false; } float pendingRequestLastActivityAtLocked = GetPendingRequestLastActivityAtLocked(transport); if (pendingRequestLastActivityAtLocked > 0f) { return Time.realtimeSinceStartup - pendingRequestLastActivityAtLocked >= 5f; } return false; } private static void AbortPendingInboundTransferLocked(DomainTransport transport, string message, bool retryAsFull) { string hash = transport.DesiredPayloadManifest.Hash; ClearPendingInboundTransferLocked(transport); DropNSpawnPlugin.DropNSpawnLogger.LogWarning((object)message); if (!string.IsNullOrWhiteSpace(hash)) { if (retryAsFull) { TryRequestFullFallbackOnceLocked(transport, hash, "source=delta_transfer_validation detail=" + message); } else { EnsurePayloadRequestedLocked(transport, transport.DesiredPayloadManifest); } } } private static bool TryRequestFullFallbackOnceLocked(DomainTransport transport, string hash, string reason) { if (string.IsNullOrWhiteSpace(hash)) { return false; } if (string.Equals(transport.FullFallbackAttemptedHash, hash, StringComparison.Ordinal)) { BlockManifestLocked(transport, hash, reason + " source=delta_full_fallback_exhausted fullFallbackAttempted=true"); return false; } transport.FullFallbackAttemptedHash = hash; return EnsurePayloadRequestedLocked(transport, transport.DesiredPayloadManifest, "", isFullFallbackRequest: true); } private static void CommitProcessedPayloadLocked(DomainTransport transport, int version, string hash, byte[] payloadBytes, List? entries, PayloadEntryIndex? payloadIndex) { lock (Sync) { if (IsProcessingResultCurrentLocked(transport, version, hash) && string.Equals(transport.DesiredPayloadManifest.Hash, hash, StringComparison.Ordinal)) { transport.AvailableHash = hash; transport.AvailablePayloadBytes = payloadBytes; transport.AvailableEntries = entries; transport.AvailablePayloadIndex = payloadIndex; ClearBlockedManifestLocked(transport); ClearPendingInboundTransferLocked(transport); transport.ProcessingInFlight = false; transport.ProcessingHash = ""; QueueReloadActionLocked(transport); } } } private static void QueueFetchedPayloadCacheWriteLocked(DomainTransport transport, string hash, byte[] payloadBytes, byte[]? compressedBytes, int compressedLength, bool requiresCompression, int roleEpoch) { if (string.IsNullOrWhiteSpace(hash)) { return; } QueueCachePersistenceJobLocked(delegate { try { if (IsCurrentRoleEpoch(roleEpoch)) { if (requiresCompression) { byte[] array = CompressBytes(payloadBytes); if (array.Length != 0 && IsCurrentRoleEpoch(roleEpoch)) { WriteCacheFile(transport.CacheDirectoryName, hash, array); } } else { byte[] array2 = compressedBytes ?? Array.Empty(); if (array2.Length != 0 && compressedLength > 0 && IsCurrentRoleEpoch(roleEpoch)) { WriteCacheFile(transport.CacheDirectoryName, hash, array2, compressedLength); } } } } catch (Exception ex) { DropNSpawnPlugin.DropNSpawnLogger.LogWarning((object)("Failed to persist synchronized " + transport.DisplayName + " payload '" + hash + "' to cache. " + ex.Message)); } }, roleEpoch); } private static bool EnsurePayloadRequestedLocked(DomainTransport transport, PayloadManifest manifest, string? baseHashOverride = null, bool isFullFallbackRequest = false) { //IL_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: Expected O, but got Unknown if (manifest.IsEmpty || IsManifestBlockedLocked(transport, manifest.Hash) || (Object)(object)_host == (Object)null || ZRoutedRpc.instance == null) { return false; } if (transport.RequestInFlight && MatchesCurrentPendingRequestLocked(transport, manifest.Hash) && !HasPendingRequestTimedOutLocked(transport)) { return false; } if (!transport.RequestInFlight && !HasAvailableClientRequestSlotLocked(transport)) { return false; } if (!TryGetCurrentServerRoutedSender(out var sender)) { return false; } string requestedBaseHash = NormalizeBaseHash(baseHashOverride ?? ((transport.AvailablePayloadBytes != null && transport.AvailableHash.Length > 0 && !string.Equals(transport.AvailableHash, manifest.Hash, StringComparison.Ordinal)) ? transport.AvailableHash : "")); string resolvedBaseHash; long num = BeginPendingInboundTransferLocked(transport, manifest.Hash, requestedBaseHash, isFullFallbackRequest, sender, out resolvedBaseHash); ZPackage val = new ZPackage(); val.Write(transport.DomainKey); val.Write(manifest.Hash); val.Write(resolvedBaseHash); val.Write(num); ZRoutedRpc.instance.InvokeRoutedRPC(0L, "DropNSpawn Payload Request", new object[1] { val }); return true; } private static void EnqueuePublishedPayloadChunksLocked(DomainTransport transport, long sender, string requestedHash, string? baseHash, long requestId) { TransferArtifact orCreateTransferArtifactLocked = GetOrCreateTransferArtifactLocked(transport, requestedHash, baseHash); if (orCreateTransferArtifactLocked != null && orCreateTransferArtifactLocked.ChunkCount > 0) { UpsertOutboundTransferLocked(transport.DomainKey, sender, requestedHash, requestId, orCreateTransferArtifactLocked); } } private static TransferArtifact? GetOrCreateTransferArtifactLocked(DomainTransport transport, string requestedHash, string? baseHash) { if (transport.PublishedCompressedBytes == null || transport.PublishedPayloadManifest.IsEmpty || !string.Equals(transport.PublishedPayloadManifest.Hash, requestedHash, StringComparison.Ordinal)) { return null; } string text = (string.IsNullOrWhiteSpace(baseHash) ? "" : (baseHash ?? "").Trim()); if (TryGetPublishedTransferArtifactLocked(transport, requestedHash, text, out TransferArtifact artifact)) { return artifact; } TransferArtifact? result = EnsureFullTransferArtifactLocked(transport, requestedHash); if (text.Length > 0 && transport.EnableArtifactPrewarm) { QueueTransferArtifactBuildIfNeededLocked(transport, requestedHash, text); } TrimTransportCachesLocked(transport); TrimAllPublishedCachesLocked(); return result; } private static bool HasPendingClientRequestWorkLocked() { IDomainTransport[] allTransports = AllTransports; for (int i = 0; i < allTransports.Length; i++) { if (allTransports[i].HasWaitingRequest()) { return true; } } return false; } private static bool HasWaitingRequestLocked(DomainTransport transport) { if (transport.DesiredPayloadManifest.IsEmpty) { return false; } if (IsManifestBlockedLocked(transport, transport.DesiredPayloadManifest.Hash)) { return false; } if (string.Equals(transport.AvailableHash, transport.DesiredPayloadManifest.Hash, StringComparison.Ordinal) && transport.AvailablePayloadBytes != null) { return false; } if (transport.ProcessingInFlight && string.Equals(transport.ProcessingHash, transport.DesiredPayloadManifest.Hash, StringComparison.Ordinal)) { return false; } if (transport.RequestInFlight && MatchesCurrentPendingRequestLocked(transport, transport.DesiredPayloadManifest.Hash) && !HasPendingRequestTimedOutLocked(transport)) { return false; } return true; } private static int CountClientRequestsInFlightLocked(bool usesLargeRequestLane) { int num = 0; IDomainTransport[] allTransports = AllTransports; foreach (IDomainTransport domainTransport in allTransports) { num += domainTransport.CountClientRequestInFlight(usesLargeRequestLane); } return num; } private static int CountClientRequestInFlightLocked(DomainTransport transport) { if (!transport.RequestInFlight) { return 0; } if (HasPendingRequestTimedOutLocked(transport)) { return 0; } return 1; } private static int CountClientRequestInFlightLocked(DomainTransport transport, bool usesLargeRequestLane) { if (transport.UsesLargeRequestLane != usesLargeRequestLane) { return 0; } return CountClientRequestInFlightLocked(transport); } private static bool HasAvailableClientRequestSlotLocked(DomainTransport transport) { if (transport.UsesLargeRequestLane) { return CountClientRequestsInFlightLocked(usesLargeRequestLane: true) < 1; } return CountClientRequestsInFlightLocked(usesLargeRequestLane: false) < 2; } private static bool TryStartNextWaitingPayloadRequestLocked() { if ((Object)(object)_host == (Object)null || ZRoutedRpc.instance == null) { return false; } bool flag = CountClientRequestsInFlightLocked(usesLargeRequestLane: true) < 1; bool flag2 = CountClientRequestsInFlightLocked(usesLargeRequestLane: false) < 2; if (!flag && !flag2) { return false; } if (flag2 && TryStartNextWaitingPayloadRequestLocked(SmallLaneRequestPriorityTransports)) { return true; } if (flag) { return TryStartNextWaitingPayloadRequestLocked(LargeLaneRequestPriorityTransports); } return false; } private static bool TryStartNextWaitingPayloadRequestLocked(IEnumerable transports) { foreach (IDomainTransport transport in transports) { if (transport.TryStartDesiredPayloadRequest()) { return true; } } return false; } private static bool TryStartDesiredPayloadRequestLocked(DomainTransport transport) { if (!HasWaitingRequestLocked(transport)) { return false; } return EnsurePayloadRequestedLocked(transport, transport.DesiredPayloadManifest); } private static bool IsProcessingResultCurrentLocked(DomainTransport transport, int version, string hash) { if (transport.ProcessingVersion == version) { return string.Equals(transport.ProcessingHash, hash, StringComparison.Ordinal); } return false; } private static void ReceivePayloadChunkLocked(DomainTransport transport, long sender, string hash, long requestId, int transferKind, string baseHash, int chunkIndex, int chunkCount, int compressedSize, byte[] chunkBytes) { PendingInboundTransfer pendingTransfer = transport.PendingTransfer; if (string.IsNullOrWhiteSpace(hash) || chunkCount <= 0 || compressedSize <= 0 || chunkIndex < 0 || chunkIndex >= chunkCount || pendingTransfer == null || (pendingTransfer.ExpectedSender != 0L && pendingTransfer.ExpectedSender != sender)) { return; } if (requestId != 0L) { if (pendingTransfer.RequestId != requestId) { return; } } else if (pendingTransfer.RequestId == 0L) { return; } if (!string.Equals(pendingTransfer.Hash, hash, StringComparison.Ordinal)) { AbortPendingInboundTransferLocked(transport, "Discarding synchronized " + transport.DisplayName + " payload '" + hash + "' because the inbound request hash does not match the active request.", retryAsFull: true); return; } string text = NormalizeBaseHash(baseHash); if (pendingTransfer.TransferKind < 0) { if (!TryInitializePendingInboundTransferBufferLocked(transport, pendingTransfer, hash, transferKind, text, chunkCount, compressedSize, chunkIndex, (chunkBytes ?? Array.Empty()).Length, out string failureReason)) { AbortPendingInboundTransferLocked(transport, "Discarding synchronized " + transport.DisplayName + " payload '" + hash + "' because " + failureReason, retryAsFull: true); return; } } else if (pendingTransfer.TransferKind != transferKind || !string.Equals(pendingTransfer.BaseHash, text, StringComparison.Ordinal) || pendingTransfer.ChunkCount != chunkCount || pendingTransfer.ExpectedCompressedSize != compressedSize) { AbortPendingInboundTransferLocked(transport, $"Discarding synchronized {transport.DisplayName} payload '{hash}' because chunk descriptor {transferKind}/{text}/{chunkCount}/{compressedSize} did not match the active transfer {pendingTransfer.TransferKind}/{pendingTransfer.BaseHash}/{pendingTransfer.ChunkCount}/{pendingTransfer.ExpectedCompressedSize}.", retryAsFull: true); return; } byte[] chunkBytes2 = chunkBytes ?? Array.Empty(); if (!TryBufferInboundChunkBytes(pendingTransfer, chunkIndex, chunkBytes2, out bool isDuplicate, out string failureReason2)) { AbortPendingInboundTransferLocked(transport, "Discarding synchronized " + transport.DisplayName + " payload '" + hash + "' because " + failureReason2, retryAsFull: true); return; } if (isDuplicate) { pendingTransfer.LastProgressAt = Time.realtimeSinceStartup; return; } pendingTransfer.LastProgressAt = Time.realtimeSinceStartup; if (pendingTransfer.ReceivedChunkCount < pendingTransfer.ChunkCount) { return; } int version = ++transport.ProcessingVersion; transport.ProcessingInFlight = true; transport.ProcessingHash = hash; int roleEpoch = _networkRoleEpoch; PayloadEntryIndex basePayloadIndexSnapshot = ((pendingTransfer.TransferKind == 1 && string.Equals(transport.AvailableHash, pendingTransfer.BaseHash, StringComparison.Ordinal)) ? transport.AvailablePayloadIndex : null); byte[] basePayloadBytesSnapshot = ((pendingTransfer.TransferKind == 1 && string.Equals(transport.AvailableHash, pendingTransfer.BaseHash, StringComparison.Ordinal) && transport.AvailablePayloadBytes != null) ? transport.AvailablePayloadBytes : null); int num; if (pendingTransfer.TransferKind != 1) { byte[]? availablePayloadBytes = transport.AvailablePayloadBytes; num = Math.Max(0, (availablePayloadBytes != null) ? availablePayloadBytes.Length : 0); } else { byte[] array = basePayloadBytesSnapshot; int val; if (array == null) { byte[]? availablePayloadBytes2 = transport.AvailablePayloadBytes; val = ((availablePayloadBytes2 != null) ? availablePayloadBytes2.Length : 0); } else { val = array.Length; } num = Math.Max(0, val); } int transferDecompressionCapacityHint = num; byte[] compressedBuffer = pendingTransfer.CompressedBuffer; int compressedLength = GetInboundCompressedLength(pendingTransfer); ClearPendingInboundTransferLocked(transport); QueueCriticalPayloadProcessingJobLocked(delegate { try { byte[] array2 = DecompressBytes(compressedBuffer, compressedLength, transferDecompressionCapacityHint); List entries = null; PayloadEntryIndex payloadIndex = null; byte[] payloadBytes; if (pendingTransfer.TransferKind == 1) { payloadBytes = ApplyDeltaPayloadBytes(transport, pendingTransfer.BaseHash, hash, array2, basePayloadIndexSnapshot, basePayloadBytesSnapshot, out List mergedEntries); entries = mergedEntries; } else { payloadBytes = array2; } EnsurePayloadSizeWithinLimit(payloadBytes.Length, transport.DisplayName); if (!string.Equals(ComputeSha256(payloadBytes), hash, StringComparison.Ordinal)) { throw new InvalidDataException("Discarding synchronized " + transport.DisplayName + " payload '" + hash + "' because the fetched hash does not match."); } if (entries != null) { BuildPayloadIndex(transport, entries, hash, out payloadIndex); } QueueFetchedPayloadCacheWriteLocked(transport, hash, payloadBytes, compressedBuffer, compressedLength, pendingTransfer.TransferKind == 1, roleEpoch); QueueMainThreadPayloadCommitLocked(delegate { CommitProcessedPayloadLocked(transport, version, hash, payloadBytes, entries, payloadIndex); }, roleEpoch); } catch (Exception ex) { Exception ex2 = ex; Exception ex3 = ex2; QueueMainThreadPayloadCommitLocked(delegate { lock (Sync) { if (IsProcessingResultCurrentLocked(transport, version, hash)) { transport.ProcessingInFlight = false; transport.ProcessingHash = ""; if (pendingTransfer.TransferKind == 1) { DropNSpawnPlugin.DropNSpawnLogger.LogWarning((object)("Failed to apply synchronized " + transport.DisplayName + " delta payload '" + hash + "'. Retrying as a full payload. " + ex3.Message)); TryRequestFullFallbackOnceLocked(transport, hash, "source=delta_apply error=" + ex3.Message); } else if (pendingTransfer.IsFullFallbackRequest) { BlockManifestLocked(transport, hash, "source=full_after_delta_fallback error=" + ex3.Message + " fullFallbackAttempted=true"); } else { DropNSpawnPlugin.DropNSpawnLogger.LogWarning((object)("Failed to assemble synchronized " + transport.DisplayName + " payload '" + hash + "'. Retrying. " + ex3.Message)); EnsurePayloadRequestedLocked(transport, transport.DesiredPayloadManifest, ""); } } } }, roleEpoch); } }, roleEpoch); } private static string ComputeEventPayloadSignature(List entries) { return EventCodec.Schema.ComputePayloadSignature(entries); } internal static string ComputeEventConfigurationSignature(IEnumerable? entries) { return ComputeEventPayloadSignature((entries ?? Enumerable.Empty()).ToList()); } private static void WriteEventConditionsDefinition(PayloadSignatureBuilder builder, EventConditionsDefinition definition) { WriteStringList(builder, definition.Biomes); WriteStringList(builder, definition.PlayerBase); WriteStringList(builder, definition.RequiredEnvironments); WriteStringList(builder, definition.Players); WriteStringList(builder, definition.RequiredGlobalKeys); WriteStringList(builder, definition.ForbiddenGlobalKeys); WriteStringList(builder, definition.RequiredKnownItems); WriteStringList(builder, definition.ForbiddenKnownItems); WriteStringList(builder, definition.RequiredPlayerKeysAny); WriteStringList(builder, definition.RequiredPlayerKeysAll); WriteStringList(builder, definition.ForbiddenPlayerKeys); } private static void WriteEventSpawnDefinition(PayloadSignatureBuilder builder, EventSpawnDefinition definition, bool includeResolvedBiomeMask) { WriteNullableString(builder, definition.Prefab); WriteNullableBool(builder, definition.Enabled); WriteOptional(builder, definition.SpawnSystem, delegate(PayloadSignatureBuilder fieldBuilder, SpawnSystemSpawnDefinition value) { WriteSpawnSystemSpawnDefinition(fieldBuilder, value, includeResolvedBiomeMask); }); } private static void WriteEventConditionsDefinition(ZPackage package, EventConditionsDefinition definition) { WriteStringList(package, definition.Biomes); WriteStringList(package, definition.PlayerBase); WriteStringList(package, definition.RequiredEnvironments); WriteStringList(package, definition.Players); WriteStringList(package, definition.RequiredGlobalKeys); WriteStringList(package, definition.ForbiddenGlobalKeys); WriteStringList(package, definition.RequiredKnownItems); WriteStringList(package, definition.ForbiddenKnownItems); WriteStringList(package, definition.RequiredPlayerKeysAny); WriteStringList(package, definition.RequiredPlayerKeysAll); WriteStringList(package, definition.ForbiddenPlayerKeys); } private static EventConditionsDefinition ReadEventConditionsDefinition(ZPackage package) { return new EventConditionsDefinition { Biomes = ReadStringList(package), PlayerBase = ReadStringList(package), RequiredEnvironments = ReadStringList(package), Players = ReadStringList(package), RequiredGlobalKeys = ReadStringList(package), ForbiddenGlobalKeys = ReadStringList(package), RequiredKnownItems = ReadStringList(package), ForbiddenKnownItems = ReadStringList(package), RequiredPlayerKeysAny = ReadStringList(package), RequiredPlayerKeysAll = ReadStringList(package), ForbiddenPlayerKeys = ReadStringList(package) }; } private static void WriteEventSpawnDefinition(ZPackage package, EventSpawnDefinition definition) { WriteNullableString(package, definition.Prefab); WriteNullableBool(package, definition.Enabled); WriteOptional(package, definition.SpawnSystem, WriteSpawnSystemSpawnDefinition); } private static EventSpawnDefinition ReadEventSpawnDefinition(ZPackage package) { return new EventSpawnDefinition { Prefab = ReadNullableString(package), Enabled = ReadNullableBool(package), SpawnSystem = ReadOptional(package, ReadSpawnSystemSpawnDefinition) }; } private static void ClearAvailablePayloadLocked(DomainTransport transport, bool deleteCacheFile = false) { string availableHash = transport.AvailableHash; transport.AvailableHash = ""; transport.AvailablePayloadBytes = null; transport.AvailableEntries = null; transport.AvailablePayloadIndex = null; if (deleteCacheFile && !string.IsNullOrWhiteSpace(availableHash)) { DeleteCacheFileIfPresent(transport.CacheDirectoryName, availableHash); } } private static void InvalidateMalformedAvailablePayloadLocked(DomainTransport transport, string hash, string reason, Exception? ex = null) { ClearAvailablePayloadLocked(transport, deleteCacheFile: true); if (!string.IsNullOrWhiteSpace(hash) && string.Equals(transport.DesiredPayloadManifest.Hash, hash, StringComparison.Ordinal)) { BlockManifestLocked(transport, hash, reason); return; } string text = ((ex == null) ? "" : $" {ex}"); DropNSpawnPlugin.DropNSpawnLogger.LogError((object)("Failed to deserialize in-memory synchronized " + transport.DisplayName + " payload '" + hash + "'. Clearing the cached client payload state." + text)); } private static void ClearInvalidManifestLocked(DomainTransport transport) { transport.InvalidDesiredManifest = ""; } private static bool TryPreserveLastKnownGoodOnInvalidManifestLocked(DomainTransport transport, string? manifestRaw, out List entries, out string payloadToken, out bool shouldLog) { string text = manifestRaw ?? ""; shouldLog = !string.Equals(transport.InvalidDesiredManifest, text, StringComparison.Ordinal); transport.InvalidDesiredManifest = text; transport.DesiredManifest = text; transport.DesiredPayloadManifest = new PayloadManifest(); ClearBlockedManifestLocked(transport); ClearPendingInboundTransferLocked(transport); transport.ProcessingInFlight = false; transport.ProcessingHash = ""; transport.ProcessingVersion++; if (TryGetAvailableEntriesLocked(transport, out entries)) { payloadToken = transport.AvailableHash; return true; } entries = new List(); payloadToken = transport.AvailableHash; return false; } private static void ClearBlockedManifestLocked(DomainTransport transport) { transport.BlockedManifestHash = ""; transport.BlockedManifestReason = ""; } private static void RefreshBlockedManifestForDesiredHashLocked(DomainTransport transport, string desiredHash) { if (string.IsNullOrWhiteSpace(desiredHash) || !string.Equals(transport.BlockedManifestHash, desiredHash, StringComparison.Ordinal)) { ClearBlockedManifestLocked(transport); } if (string.IsNullOrWhiteSpace(desiredHash) || !string.Equals(transport.FullFallbackAttemptedHash, desiredHash, StringComparison.Ordinal)) { transport.FullFallbackAttemptedHash = ""; } } private static bool IsManifestBlockedLocked(DomainTransport transport, string hash) { if (!string.IsNullOrWhiteSpace(hash)) { return string.Equals(transport.BlockedManifestHash, hash, StringComparison.Ordinal); } return false; } private static void BlockManifestLocked(DomainTransport transport, string hash, string reason) { transport.ProcessingInFlight = false; transport.ProcessingHash = ""; transport.ProcessingVersion++; ClearPendingInboundTransferLocked(transport); transport.RequestInFlight = false; transport.RequestStartedAt = 0f; transport.BlockedManifestHash = hash ?? ""; transport.BlockedManifestReason = reason ?? ""; DeleteCacheFileIfPresent(transport.CacheDirectoryName, hash ?? ""); DropNSpawnPlugin.DropNSpawnLogger.LogWarning((object)("Synchronized " + transport.DisplayName + " payload '" + hash + "' is blocked until the server publishes a different manifest. desiredHash=" + ((transport.DesiredPayloadManifest.Hash.Length > 0) ? transport.DesiredPayloadManifest.Hash : "") + " reason=" + reason)); } private static void HandleManifestChanged(DomainTransport transport, string? manifestRaw) { lock (Sync) { EnsureRpcRegisteredLocked(); if (!TryParsePayloadManifest(transport, manifestRaw, out PayloadManifest manifest)) { List entries; string payloadToken; bool shouldLog; bool flag = TryPreserveLastKnownGoodOnInvalidManifestLocked(transport, manifestRaw, out entries, out payloadToken, out shouldLog); if (shouldLog) { DropNSpawnPlugin.DropNSpawnLogger.LogWarning((object)("Received invalid synchronized " + transport.DisplayName + " payload manifest '" + manifestRaw + "'. Keeping the last known good client payload state.")); } if (flag) { QueueReloadActionLocked(transport); } return; } ClearInvalidManifestLocked(transport); transport.DesiredManifest = manifestRaw ?? ""; transport.DesiredPayloadManifest = manifest; RefreshBlockedManifestForDesiredHashLocked(transport, manifest.Hash); CancelProcessingIfHashMismatchLocked(transport, manifest.Hash); if (manifest.IsEmpty) { transport.AvailableHash = ""; transport.AvailablePayloadBytes = null; transport.AvailableEntries = null; transport.AvailablePayloadIndex = null; ClearPendingInboundTransferLocked(transport); transport.ProcessingInFlight = false; transport.ProcessingHash = ""; transport.ProcessingVersion++; QueueReloadActionLocked(transport); } else if (string.Equals(transport.AvailableHash, manifest.Hash, StringComparison.Ordinal) && transport.AvailablePayloadBytes != null) { QueueReloadActionLocked(transport); } else if (!IsManifestBlockedLocked(transport, manifest.Hash) && !TryScheduleCachedPayloadLoadLocked(transport, manifest)) { EnsurePayloadRequestedLocked(transport, manifest); } } } private static bool TryScheduleCachedPayloadLoadLocked(DomainTransport transport, PayloadManifest manifest) { if (manifest.IsEmpty) { return false; } if (IsManifestBlockedLocked(transport, manifest.Hash)) { return false; } if (transport.ProcessingInFlight && string.Equals(transport.ProcessingHash, manifest.Hash, StringComparison.Ordinal)) { return true; } if (!File.Exists(GetCachePath(transport.CacheDirectoryName, manifest.Hash))) { return false; } int version = ++transport.ProcessingVersion; transport.ProcessingInFlight = true; transport.ProcessingHash = manifest.Hash; int roleEpoch = _networkRoleEpoch; QueueCriticalPayloadProcessingJobLocked(delegate { try { if (!TryReadCachedPayloadBytes(transport.CacheDirectoryName, transport.DisplayName, manifest.Hash, out byte[] payloadBytes, out byte[] _)) { QueueMainThreadPayloadCommitLocked(delegate { lock (Sync) { if (IsProcessingResultCurrentLocked(transport, version, manifest.Hash)) { transport.ProcessingInFlight = false; transport.ProcessingHash = ""; EnsurePayloadRequestedLocked(transport, transport.DesiredPayloadManifest); } } }, roleEpoch); } else { QueueMainThreadPayloadCommitLocked(delegate { CommitProcessedPayloadLocked(transport, version, manifest.Hash, payloadBytes, null, null); }, roleEpoch); } } catch (Exception ex) { Exception ex2 = ex; Exception ex3 = ex2; QueueMainThreadPayloadCommitLocked(delegate { lock (Sync) { if (IsProcessingResultCurrentLocked(transport, version, manifest.Hash)) { transport.ProcessingInFlight = false; transport.ProcessingHash = ""; DropNSpawnPlugin.DropNSpawnLogger.LogWarning((object)("Failed to read cached " + transport.DisplayName + " payload '" + manifest.Hash + "'. " + ex3.Message)); EnsurePayloadRequestedLocked(transport, transport.DesiredPayloadManifest); } } }, roleEpoch); } }, roleEpoch); return true; } private static void CancelProcessingIfHashMismatchLocked(DomainTransport transport, string desiredHash) { if (transport.ProcessingInFlight && !string.Equals(transport.ProcessingHash, desiredHash, StringComparison.Ordinal)) { transport.ProcessingInFlight = false; transport.ProcessingHash = ""; transport.ProcessingVersion++; } } private static void QueueCriticalPayloadProcessingJobLocked(Action job) { QueueCriticalPayloadProcessingJobLocked(job, _networkRoleEpoch); } private static void QueueCriticalPayloadProcessingJobLocked(Action job, int roleEpoch) { PendingCriticalPayloadProcessingJobs.Enqueue(new PendingPayloadProcessingJob { RoleEpoch = roleEpoch, Action = job }); EnsurePayloadProcessingWorkersLocked(); } private static void QueueDeltaArtifactPrewarmJobLocked(Action job) { QueueDeltaArtifactPrewarmJobLocked(job, _networkRoleEpoch); } private static void QueueDeltaArtifactPrewarmJobLocked(Action job, int roleEpoch) { PendingDeltaArtifactPrewarmJobs.Enqueue(new PendingPayloadProcessingJob { RoleEpoch = roleEpoch, Action = job }); EnsurePayloadProcessingWorkersLocked(); } private static void QueueCachePersistenceJobLocked(Action job) { QueueCachePersistenceJobLocked(job, _networkRoleEpoch); } private static void QueueCachePersistenceJobLocked(Action job, int roleEpoch) { PendingCachePersistenceJobs.Enqueue(new PendingPayloadProcessingJob { RoleEpoch = roleEpoch, Action = job }); EnsurePayloadProcessingWorkersLocked(); } private static void EnsurePayloadProcessingWorkersLocked() { int num = Math.Min(3, PendingCriticalPayloadProcessingJobs.Count + Math.Min(1, PendingDeltaArtifactPrewarmJobs.Count + PendingCachePersistenceJobs.Count)); while (_payloadProcessingWorkersRunning < num) { _payloadProcessingWorkersRunning++; ThreadPool.QueueUserWorkItem(delegate { ProcessPayloadProcessingJobsWorker(); }); } } private static void ProcessPayloadProcessingJobsWorker() { while (true) { Action action = null; lock (Sync) { PendingPayloadProcessingJob item = null; if (!PendingCriticalPayloadProcessingJobs.TryDequeue(out item) && !PendingDeltaArtifactPrewarmJobs.TryDequeue(out item) && !PendingCachePersistenceJobs.TryDequeue(out item)) { _payloadProcessingWorkersRunning = Math.Max(0, _payloadProcessingWorkersRunning - 1); break; } if (item != null && item.RoleEpoch == _networkRoleEpoch) { action = item.Action; } } if (action != null) { try { action(); } catch (Exception ex) { DropNSpawnPlugin.DropNSpawnLogger.LogWarning((object)("Unhandled background payload processing failure. " + ex.Message)); } } } } private static bool IsCurrentRoleEpoch(int roleEpoch) { lock (Sync) { return roleEpoch == _networkRoleEpoch; } } private static void QueueMainThreadPayloadCommitLocked(Action action) { QueueMainThreadPayloadCommitLocked(action, _networkRoleEpoch); } private static void QueueMainThreadPayloadCommitLocked(Action action, int roleEpoch) { lock (Sync) { PendingMainThreadPayloadCommits.Enqueue(new PendingMainThreadPayloadCommit { RoleEpoch = roleEpoch, Action = action }); } } private static void QueueReloadActionLocked(DomainTransport transport) { string item = BuildPendingReloadDomainKey(transport.DomainKey, _networkRoleEpoch); if (PendingReloadDomainKeys.Add(item)) { PendingReloadActions.Enqueue(new PendingReloadAction { RoleEpoch = _networkRoleEpoch, DomainKey = transport.DomainKey, Action = transport.ReloadAction }); } } private static bool TryDequeueCurrentMainThreadPayloadCommitLocked(out Action? action) { action = null; PendingMainThreadPayloadCommit item; while (PendingMainThreadPayloadCommits.TryDequeue(out item)) { if (item != null && item.RoleEpoch == _networkRoleEpoch) { action = item.Action; return true; } } return false; } private static bool TryDequeueCurrentReloadActionLocked(out PendingReloadAction? pendingReload) { pendingReload = null; PendingReloadAction item; while (PendingReloadActions.TryDequeue(out item)) { if (item != null) { PendingReloadDomainKeys.Remove(BuildPendingReloadDomainKey(item.DomainKey, item.RoleEpoch)); if (item.RoleEpoch == _networkRoleEpoch) { pendingReload = item; return true; } } } return false; } private static string BuildPendingReloadDomainKey(string domainKey, int roleEpoch) { return roleEpoch.ToString(CultureInfo.InvariantCulture) + ":" + domainKey; } private static void RememberPublishedPayloadLocked(DomainTransport transport, string hash, byte[]? payloadBytes, PayloadEntryIndex? payloadIndex) { if (!string.IsNullOrWhiteSpace(hash) && payloadBytes != null && payloadBytes.Length != 0) { if (!transport.PublishedPayloadHistory.TryGetValue(hash, out PublishedPayloadHistoryEntry value)) { value = new PublishedPayloadHistoryEntry { Hash = hash }; transport.PublishedPayloadHistory[hash] = value; } int estimatedBytes = value.EstimatedBytes; value.PayloadBytes = payloadBytes; value.PayloadIndex = payloadIndex; value.EstimatedBytes = EstimatePublishedPayloadHistoryEntryBytes(hash, payloadBytes, payloadIndex); AdjustPublishedPayloadHistoryBytesLocked(transport, value.EstimatedBytes - estimatedBytes); LinkedListNode node = value.LruNode; TouchLruNodeLocked(transport.PublishedPayloadHistoryLru, hash, ref node); value.LruNode = node; } } private static int EstimatePublishedPayloadHistoryEntryBytes(string hash, byte[] payloadBytes, PayloadEntryIndex? payloadIndex) { return ClampByteEstimate(64L + (long)EstimateStringBytes(hash) + payloadBytes.Length + EstimatePayloadIndexBytes(payloadIndex)); } private static int EstimatePayloadIndexBytes(PayloadEntryIndex? payloadIndex) { if (payloadIndex == null) { return 0; } long num = 64L; num += (long)payloadIndex.OrderedKeys.Count * 24L; foreach (string orderedKey in payloadIndex.OrderedKeys) { num += EstimateStringBytes(orderedKey); } num += (long)payloadIndex.EntriesByKey.Count * 24L; foreach (KeyValuePair item in payloadIndex.EntrySignaturesByKey) { item.Deconstruct(out var key, out var value); string value2 = key; string value3 = value; num += 24L + (long)EstimateStringBytes(value2) + EstimateStringBytes(value3); } return ClampByteEstimate(num); } private static int EstimateTransferArtifactBytes(TransferArtifact artifact) { if (artifact == null) { return 0; } return ClampByteEstimate(64L + (long)EstimateStringBytes(artifact.BaseHash) + EstimateStringBytes(artifact.TargetHash) + artifact.CompressedBytes.Length); } private static int EstimateStringBytes(string? value) { return (value?.Length ?? 0) * 2; } private static int ClampByteEstimate(long estimatedBytes) { if (estimatedBytes > 0) { if (estimatedBytes < int.MaxValue) { return (int)estimatedBytes; } return int.MaxValue; } return 0; } private static int GetTrimTargetBytes(int budgetBytes) { if (budgetBytes <= 0) { return 0; } return Math.Max(0, budgetBytes * 85 / 100); } private static void TouchLruNodeLocked(LinkedList lru, string key, ref LinkedListNode? node) { if (key.Length != 0) { if (node == null || node.List != lru) { node = lru.AddLast(key); } else if (node.Next != null) { lru.Remove(node); lru.AddLast(node); } } } private static void RemoveLruNodeLocked(LinkedList lru, ref LinkedListNode? node) { if (node != null) { if (node.List == lru) { lru.Remove(node); } node = null; } } private static void AdjustPublishedPayloadHistoryBytesLocked(DomainTransport transport, int deltaBytes) { if (deltaBytes != 0) { transport.PublishedPayloadHistoryBytes = Math.Max(0, transport.PublishedPayloadHistoryBytes + deltaBytes); transport.PublishedCacheBytes = Math.Max(0, transport.PublishedCacheBytes + deltaBytes); } } private static void AdjustPublishedTransferArtifactBytesLocked(DomainTransport transport, int deltaBytes) { if (deltaBytes != 0) { transport.PublishedTransferArtifactBytes = Math.Max(0, transport.PublishedTransferArtifactBytes + deltaBytes); transport.PublishedCacheBytes = Math.Max(0, transport.PublishedCacheBytes + deltaBytes); } } private static void RemovePublishedPayloadHistoryEntryLocked(DomainTransport transport, PublishedPayloadHistoryEntry? entry) { if (entry != null) { transport.PublishedPayloadHistory.Remove(entry.Hash); LinkedListNode node = entry.LruNode; RemoveLruNodeLocked(transport.PublishedPayloadHistoryLru, ref node); entry.LruNode = node; AdjustPublishedPayloadHistoryBytesLocked(transport, -entry.EstimatedBytes); } } private static void RemoveTransferArtifactLocked(DomainTransport transport, TransferArtifact? artifact) { if (artifact != null) { transport.PublishedTransferArtifacts.Remove(artifact.CacheKey); LinkedListNode node = artifact.LruNode; RemoveLruNodeLocked(transport.PublishedTransferArtifactLru, ref node); artifact.LruNode = node; AdjustPublishedTransferArtifactBytesLocked(transport, -artifact.EstimatedBytes); } } private static void ClearPublishedPayloadHistoryLocked(DomainTransport transport) { transport.PublishedPayloadHistory.Clear(); transport.PublishedPayloadHistoryLru.Clear(); transport.PublishedCacheBytes = Math.Max(0, transport.PublishedCacheBytes - transport.PublishedPayloadHistoryBytes); transport.PublishedPayloadHistoryBytes = 0; } private static void ClearPublishedTransferArtifactsLocked(DomainTransport transport) { transport.PublishedTransferArtifacts.Clear(); transport.PublishedTransferArtifactLru.Clear(); transport.PublishedCacheBytes = Math.Max(0, transport.PublishedCacheBytes - transport.PublishedTransferArtifactBytes); transport.PublishedTransferArtifactBytes = 0; } private static void ClearPinnedCacheReferencesLocked(DomainTransport transport) { transport.PinnedPayloadHashRefCounts.Clear(); transport.PinnedArtifactKeyRefCounts.Clear(); } private static void IncrementRefCountLocked(Dictionary refCounts, string key) { if (!string.IsNullOrWhiteSpace(key)) { refCounts[key] = ((!refCounts.TryGetValue(key, out var value)) ? 1 : (value + 1)); } } private static void DecrementRefCountLocked(Dictionary refCounts, string key) { if (!string.IsNullOrWhiteSpace(key) && refCounts.TryGetValue(key, out var value)) { if (value <= 1) { refCounts.Remove(key); } else { refCounts[key] = value - 1; } } } private static bool IsPinnedLocked(Dictionary refCounts, string key) { if (key.Length > 0 && refCounts.TryGetValue(key, out var value)) { return value > 0; } return false; } private static bool TryTrimOneTransferArtifactLocked(DomainTransport transport) { int count = transport.PublishedTransferArtifacts.Count; LinkedListNode linkedListNode = transport.PublishedTransferArtifactLru.First; while (linkedListNode != null && count-- > 0) { LinkedListNode linkedListNode2 = linkedListNode; linkedListNode = linkedListNode.Next; string value = linkedListNode2.Value; if (!transport.PublishedTransferArtifacts.TryGetValue(value, out TransferArtifact value2) || value2 == null) { transport.PublishedTransferArtifactLru.Remove(linkedListNode2); continue; } if (IsPinnedLocked(transport.PinnedArtifactKeyRefCounts, value)) { transport.PublishedTransferArtifactLru.Remove(linkedListNode2); transport.PublishedTransferArtifactLru.AddLast(linkedListNode2); continue; } RemoveTransferArtifactLocked(transport, value2); return true; } return false; } private static bool TryTrimOnePayloadHistoryEntryLocked(DomainTransport transport) { int count = transport.PublishedPayloadHistory.Count; LinkedListNode linkedListNode = transport.PublishedPayloadHistoryLru.First; while (linkedListNode != null && count-- > 0) { LinkedListNode linkedListNode2 = linkedListNode; linkedListNode = linkedListNode.Next; string value = linkedListNode2.Value; if (!transport.PublishedPayloadHistory.TryGetValue(value, out PublishedPayloadHistoryEntry value2) || value2 == null) { transport.PublishedPayloadHistoryLru.Remove(linkedListNode2); continue; } if (IsPinnedLocked(transport.PinnedPayloadHashRefCounts, value)) { transport.PublishedPayloadHistoryLru.Remove(linkedListNode2); transport.PublishedPayloadHistoryLru.AddLast(linkedListNode2); continue; } RemovePublishedPayloadHistoryEntryLocked(transport, value2); return true; } return false; } private static void TrimTransportCachesLocked(DomainTransport transport) { int publishedCacheBudgetBytes = transport.PublishedCacheBudgetBytes; if (publishedCacheBudgetBytes > 0 && transport.PublishedCacheBytes > publishedCacheBudgetBytes) { int trimTargetBytes = GetTrimTargetBytes(publishedCacheBudgetBytes); int num = transport.PublishedPayloadHistory.Count + transport.PublishedTransferArtifacts.Count; while (transport.PublishedCacheBytes > trimTargetBytes && num-- > 0 && (TryTrimOneTransferArtifactLocked(transport) || TryTrimOnePayloadHistoryEntryLocked(transport))) { } } } private static int GetTotalPublishedCacheBytesLocked() { int num = 0; IDomainTransport[] allTransports = AllTransports; foreach (IDomainTransport domainTransport in allTransports) { num += domainTransport.PublishedCacheBytes; } return num; } private static bool TryTrimAnyTransportCacheOnceLocked() { foreach (IDomainTransport item in AllTransports.OrderByDescending((IDomainTransport transport) => transport.PublishedCacheBytes)) { if (item.PublishedCacheBytes > 0 && item.TryTrimPublishedCacheOnce()) { return true; } } return false; } private static bool TryTrimTransportCacheOnceLocked(DomainTransport transport) { if (!TryTrimOneTransferArtifactLocked(transport)) { return TryTrimOnePayloadHistoryEntryLocked(transport); } return true; } private static void TrimAllPublishedCachesLocked() { int totalPublishedCacheBytesLocked = GetTotalPublishedCacheBytesLocked(); if (totalPublishedCacheBytesLocked > 67108864) { int trimTargetBytes = GetTrimTargetBytes(67108864); int num = 0; IDomainTransport[] allTransports = AllTransports; foreach (IDomainTransport domainTransport in allTransports) { num += domainTransport.PublishedPayloadHistoryItemCount + domainTransport.PublishedTransferArtifactCount; } while (totalPublishedCacheBytesLocked > trimTargetBytes && num-- > 0 && TryTrimAnyTransportCacheOnceLocked()) { totalPublishedCacheBytesLocked = GetTotalPublishedCacheBytesLocked(); } } } private static bool BuildPayloadIndex(DomainTransport transport, List entries, string payloadHash, out PayloadEntryIndex? payloadIndex) { payloadIndex = new PayloadEntryIndex(); foreach (TEntry entry in entries) { string text = transport.KeySelector(entry); if (text.Length == 0) { DropNSpawnPlugin.DropNSpawnLogger.LogWarning((object)("Skipping synchronized " + transport.DisplayName + " delta support for payload '" + payloadHash + "' because it contains an empty entry key.")); payloadIndex = null; return false; } if (payloadIndex.EntriesByKey.ContainsKey(text)) { DropNSpawnPlugin.DropNSpawnLogger.LogWarning((object)("Skipping synchronized " + transport.DisplayName + " delta support for payload '" + payloadHash + "' because it contains duplicate key '" + text + "'.")); payloadIndex = null; return false; } payloadIndex.OrderedKeys.Add(text); payloadIndex.EntriesByKey[text] = entry; payloadIndex.EntrySignaturesByKey[text] = transport.EntrySignatureBuilder(entry); } return true; } private static bool TryEnsurePayloadIndex(DomainTransport transport, string payloadHash, byte[] payloadBytes, PayloadEntryIndex? existingIndex, out PayloadEntryIndex? payloadIndex) { if (existingIndex != null) { payloadIndex = existingIndex; return true; } List entries = transport.Deserializer(payloadBytes); return BuildPayloadIndex(transport, entries, payloadHash, out payloadIndex); } private static bool TryBuildDeltaPayloadBytes(DomainTransport transport, string baseHash, string targetHash, PayloadEntryIndex basePayloadIndex, PayloadEntryIndex targetPayloadIndex, out byte[] deltaPayloadBytes) { //IL_01c5: Unknown result type (might be due to invalid IL or missing references) //IL_01cc: Expected O, but got Unknown List list = new List(targetPayloadIndex.OrderedKeys.Count); HashSet hashSet = new HashSet(StringComparer.OrdinalIgnoreCase); List list2 = new List(); foreach (string orderedKey in targetPayloadIndex.OrderedKeys) { if (!hashSet.Add(orderedKey)) { DropNSpawnPlugin.DropNSpawnLogger.LogWarning((object)("Skipping synchronized " + transport.DisplayName + " delta " + baseHash + "->" + targetHash + " because payload '" + targetHash + "' contains duplicate key '" + orderedKey + "'.")); deltaPayloadBytes = Array.Empty(); return false; } list.Add(orderedKey); if (!basePayloadIndex.EntrySignaturesByKey.TryGetValue(orderedKey, out string value) || !targetPayloadIndex.EntrySignaturesByKey.TryGetValue(orderedKey, out string value2) || !string.Equals(value, value2, StringComparison.Ordinal)) { list2.Add(targetPayloadIndex.EntriesByKey[orderedKey]); } } List list3 = new List(); foreach (string orderedKey2 in basePayloadIndex.OrderedKeys) { if (!targetPayloadIndex.EntriesByKey.ContainsKey(orderedKey2)) { list3.Add(orderedKey2); } } int num = list3.Count + list2.Count; if (num == 0) { deltaPayloadBytes = Array.Empty(); return false; } if (targetPayloadIndex.OrderedKeys.Count > 0 && (double)num > Math.Ceiling((float)targetPayloadIndex.OrderedKeys.Count * 0.5f)) { deltaPayloadBytes = Array.Empty(); return false; } ZPackage val = new ZPackage(); val.Write(1); val.Write(baseHash); val.Write(targetHash); WriteStringList(val, list); WriteStringList(val, list3); val.Write((list2.Count == 0) ? Array.Empty() : transport.Serializer(list2)); deltaPayloadBytes = val.GetArray(); return true; } private static bool TryBuildPrimaryDeltaArtifact(DomainTransport transport, string baseHash, byte[]? basePayloadBytes, PayloadEntryIndex? basePayloadIndex, string targetHash, byte[] targetPayloadBytes, PayloadEntryIndex? targetPayloadIndex, byte[] fullCompressedPayloadBytes, out PayloadEntryIndex? resolvedTargetPayloadIndex, out TransferArtifact? artifact) { resolvedTargetPayloadIndex = targetPayloadIndex; artifact = null; string text = NormalizeBaseHash(baseHash); if (text.Length == 0 || string.IsNullOrWhiteSpace(targetHash) || string.Equals(text, targetHash, StringComparison.Ordinal) || basePayloadBytes == null || basePayloadBytes.Length == 0 || targetPayloadBytes.Length == 0 || fullCompressedPayloadBytes.Length == 0) { return false; } try { if (!TryEnsurePayloadIndex(transport, targetHash, targetPayloadBytes, targetPayloadIndex, out resolvedTargetPayloadIndex) || resolvedTargetPayloadIndex == null) { return false; } if (!TryEnsurePayloadIndex(transport, text, basePayloadBytes, basePayloadIndex, out PayloadEntryIndex payloadIndex) || payloadIndex == null) { return false; } if (!TryBuildDeltaPayloadBytes(transport, text, targetHash, payloadIndex, resolvedTargetPayloadIndex, out byte[] deltaPayloadBytes)) { return false; } byte[] array = CompressBytes(deltaPayloadBytes); if (array.Length == 0 || (float)array.Length >= (float)fullCompressedPayloadBytes.Length * 0.85f) { return false; } artifact = new TransferArtifact { CacheKey = BuildTransferArtifactCacheKey(targetHash, text), TransferKind = 1, BaseHash = text, TargetHash = targetHash, CompressedBytes = array, ChunkSizeBytes = transport.ChunkSizeBytes, ChunkCount = Math.Max(1, (int)Math.Ceiling((double)array.Length / (double)transport.ChunkSizeBytes)) }; artifact.EstimatedBytes = EstimateTransferArtifactBytes(artifact); return true; } catch { return false; } } private static byte[] ApplyDeltaPayloadBytes(DomainTransport transport, string baseHash, string targetHash, byte[] deltaPayloadBytes, PayloadEntryIndex? basePayloadIndexSnapshot, byte[]? basePayloadBytesSnapshot, out List mergedEntries) { //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Expected O, but got Unknown mergedEntries = new List(); if (string.IsNullOrWhiteSpace(baseHash)) { throw new InvalidDataException("Missing base hash for synchronized " + transport.DisplayName + " delta payload '" + targetHash + "'."); } ZPackage val = new ZPackage(deltaPayloadBytes); int num = val.ReadInt(); if (num != 1) { throw new InvalidDataException($"Unsupported delta payload DTO version '{num}'."); } string text = val.ReadString(); string text2 = val.ReadString(); if (!string.Equals(baseHash, text, StringComparison.Ordinal) || !string.Equals(targetHash, text2, StringComparison.Ordinal)) { throw new InvalidDataException("Synchronized " + transport.DisplayName + " delta payload hash mismatch. Expected " + baseHash + "->" + targetHash + " but got " + text + "->" + text2 + "."); } List list = ReadStringList(val) ?? new List(); List list2 = ReadStringList(val) ?? new List(); byte[] array = val.ReadByteArray(); List list3 = ((array.Length == 0) ? new List() : transport.Deserializer(array)); Dictionary dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); if (basePayloadIndexSnapshot != null) { foreach (KeyValuePair item in basePayloadIndexSnapshot.EntriesByKey) { dictionary[item.Key] = item.Value; } } else { byte[] payloadBytes = basePayloadBytesSnapshot ?? Array.Empty(); if (payloadBytes.Length == 0 && !TryReadCachedPayloadBytes(transport.CacheDirectoryName, transport.DisplayName, baseHash, out payloadBytes, out byte[] _)) { throw new InvalidDataException("Missing cached base payload '" + baseHash + "' required to apply synchronized " + transport.DisplayName + " delta '" + targetHash + "'."); } foreach (TEntry item2 in transport.Deserializer(payloadBytes)) { dictionary[transport.KeySelector(item2)] = item2; } } foreach (string item3 in list2) { dictionary.Remove(item3); } foreach (TEntry item4 in list3) { dictionary[transport.KeySelector(item4)] = item4; } mergedEntries = new List(list.Count); foreach (string item5 in list) { if (!dictionary.TryGetValue(item5, out var value)) { throw new InvalidDataException("Synchronized " + transport.DisplayName + " delta payload '" + targetHash + "' is missing ordered entry '" + item5 + "'."); } mergedEntries.Add(value); } return transport.Serializer(mergedEntries); } private static void EnsureRpcRegisteredLocked() { if (!((Object)(object)_host == (Object)null) && ZRoutedRpc.instance != null && _registeredRpc != ZRoutedRpc.instance) { ZRoutedRpc.instance.Register("DropNSpawn Payload Request", (Action)RPC_RequestPayload); ZRoutedRpc.instance.Register("DropNSpawn Payload Chunk", (Action)RPC_ReceivePayloadChunk); _registeredRpc = ZRoutedRpc.instance; } } private static bool IsOutboundTransferPeerReady(long sender) { if (sender == 0L) { return true; } ZNet instance = ZNet.instance; if (instance == null) { return false; } ZNetPeer peer = instance.GetPeer(sender); return ((peer != null) ? new bool?(peer.IsReady()) : ((bool?)null)) == true; } private static bool IsOutboundTransferRequestEquivalent(OutboundTransferState state, string hash, long requestId, TransferArtifact artifact) { if (state != null && state.RequestId == requestId && string.Equals(state.Hash, hash, StringComparison.Ordinal) && state.Artifact != null && string.Equals(state.Artifact.TargetHash, artifact.TargetHash, StringComparison.Ordinal) && string.Equals(state.Artifact.BaseHash, artifact.BaseHash, StringComparison.Ordinal)) { return state.Artifact.TransferKind == artifact.TransferKind; } return false; } private static void EnqueueOutboundTransferKeyLocked(OutboundTransferKey key, OutboundTransferState state) { if (!state.IsQueued) { PendingOutboundTransferKeys.Enqueue(key); state.IsQueued = true; } } private static void UpdatePinnedOutboundTransferReferencesLocked(DomainTransport transport, OutboundTransferState? previousState, OutboundTransferState? nextState) { if (previousState != null) { DecrementRefCountLocked(transport.PinnedPayloadHashRefCounts, previousState.Hash); if (previousState.Artifact != null) { DecrementRefCountLocked(transport.PinnedPayloadHashRefCounts, previousState.Artifact.BaseHash); DecrementRefCountLocked(transport.PinnedArtifactKeyRefCounts, (previousState.Artifact.CacheKey.Length > 0) ? previousState.Artifact.CacheKey : BuildTransferArtifactCacheKey(previousState.Hash, previousState.Artifact.BaseHash)); } } if (nextState != null) { IncrementRefCountLocked(transport.PinnedPayloadHashRefCounts, nextState.Hash); if (nextState.Artifact != null) { IncrementRefCountLocked(transport.PinnedPayloadHashRefCounts, nextState.Artifact.BaseHash); IncrementRefCountLocked(transport.PinnedArtifactKeyRefCounts, (nextState.Artifact.CacheKey.Length > 0) ? nextState.Artifact.CacheKey : BuildTransferArtifactCacheKey(nextState.Hash, nextState.Artifact.BaseHash)); } } } private static void UpdatePinnedOutboundTransferReferencesForDomainLocked(string domainKey, OutboundTransferState? previousState, OutboundTransferState? nextState) { if (!string.IsNullOrWhiteSpace(domainKey) && TransportsByDomainKey.TryGetValue(domainKey, out IDomainTransport value)) { value.UpdatePinnedOutboundTransferReferences(previousState, nextState); } } private static void RemoveActiveOutboundTransferLocked(OutboundTransferKey key) { if (ActiveOutboundTransfers.TryGetValue(key, out OutboundTransferState value) && value != null) { UpdatePinnedOutboundTransferReferencesForDomainLocked(key.DomainKey, value, null); } ActiveOutboundTransfers.Remove(key); } private static void UpsertOutboundTransferLocked(string domainKey, long sender, string hash, long requestId, TransferArtifact artifact) { if (artifact == null) { return; } OutboundTransferKey key = new OutboundTransferKey(sender, domainKey); float realtimeSinceStartup = Time.realtimeSinceStartup; if (ActiveOutboundTransfers.TryGetValue(key, out OutboundTransferState value)) { if (value.RoleEpoch != _networkRoleEpoch || !IsOutboundTransferPeerReady(sender)) { RemoveActiveOutboundTransferLocked(key); } else { if (IsOutboundTransferRequestEquivalent(value, hash, requestId, artifact)) { value.LastRequestedAt = realtimeSinceStartup; EnqueueOutboundTransferKeyLocked(key, value); return; } if (requestId != 0L && value.RequestId != 0L && requestId < value.RequestId) { return; } } } OutboundTransferState outboundTransferState = new OutboundTransferState { RoleEpoch = _networkRoleEpoch, Hash = (hash ?? ""), RequestId = requestId, Artifact = artifact, NextChunkIndex = 0, IsQueued = (value?.IsQueued ?? false), LastRequestedAt = realtimeSinceStartup }; UpdatePinnedOutboundTransferReferencesForDomainLocked(domainKey, value, outboundTransferState); ActiveOutboundTransfers[key] = outboundTransferState; EnqueueOutboundTransferKeyLocked(key, outboundTransferState); } private static void RPC_RequestPayload(long sender, ZPackage package) { if (!DropNSpawnPlugin.IsSourceOfTruth) { return; } string text = package.ReadString(); string text2 = package.ReadString(); string baseHash = package.ReadString(); long requestId = ((package.GetPos() < package.Size()) ? package.ReadLong() : 0); if (string.IsNullOrWhiteSpace(text) || string.IsNullOrWhiteSpace(text2)) { return; } lock (Sync) { EnsureRpcRegisteredLocked(); if (TransportsByDomainKey.TryGetValue(text, out IDomainTransport value)) { value.EnqueuePublishedPayloadChunks(sender, text2, baseHash, requestId); } } } private static bool TryPrepareOutboundChunkForSendLocked(out PreparedOutboundChunk? preparedChunk) { preparedChunk = null; OutboundTransferKey item; while (PendingOutboundTransferKeys.TryDequeue(out item)) { if (!ActiveOutboundTransfers.TryGetValue(item, out OutboundTransferState value) || value == null) { continue; } value.IsQueued = false; if (value.RoleEpoch != _networkRoleEpoch || !IsOutboundTransferPeerReady(item.Sender)) { RemoveActiveOutboundTransferLocked(item); continue; } if (value.Artifact == null || value.NextChunkIndex < 0 || value.NextChunkIndex >= value.Artifact.ChunkCount) { RemoveActiveOutboundTransferLocked(item); continue; } int num = Math.Max(1, value.Artifact.ChunkSizeBytes); int num2 = value.NextChunkIndex * num; int num3 = value.Artifact.CompressedBytes.Length - num2; if (num3 <= 0) { RemoveActiveOutboundTransferLocked(item); continue; } int num4 = Math.Min(num, num3); if (_outboundBytesSentThisFrame + num4 > num * 2) { EnqueueOutboundTransferKeyLocked(item, value); return false; } preparedChunk = new PreparedOutboundChunk { Key = item, RoleEpoch = value.RoleEpoch, RequestId = value.RequestId, SourceBytes = value.Artifact.CompressedBytes, ChunkOffset = num2, ChunkLength = num4, Message = new OutboundChunkMessage { Sender = item.Sender, DomainKey = item.DomainKey, Hash = value.Hash, RequestId = value.RequestId, TransferKind = value.Artifact.TransferKind, BaseHash = value.Artifact.BaseHash, ChunkIndex = value.NextChunkIndex, ChunkCount = value.Artifact.ChunkCount, CompressedSize = value.Artifact.CompressedBytes.Length } }; return true; } return false; } private static void FinalizePreparedOutboundChunkLocked(PreparedOutboundChunk preparedChunk, bool sent) { if (preparedChunk == null || preparedChunk.RoleEpoch != _networkRoleEpoch || !DropNSpawnPlugin.IsSourceOfTruth || !ActiveOutboundTransfers.TryGetValue(preparedChunk.Key, out OutboundTransferState value) || value == null || value.RoleEpoch != _networkRoleEpoch || value.RequestId != preparedChunk.RequestId) { return; } if (!IsOutboundTransferPeerReady(preparedChunk.Key.Sender)) { RemoveActiveOutboundTransferLocked(preparedChunk.Key); return; } if (!sent) { EnqueueOutboundTransferKeyLocked(preparedChunk.Key, value); return; } _outboundBytesSentThisFrame += preparedChunk.ChunkLength; value.NextChunkIndex++; if (value.Artifact != null && value.NextChunkIndex < value.Artifact.ChunkCount) { EnqueueOutboundTransferKeyLocked(preparedChunk.Key, value); } else { RemoveActiveOutboundTransferLocked(preparedChunk.Key); } } private static bool SendOutboundChunk(PreparedOutboundChunk preparedChunk) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Expected O, but got Unknown if (ZRoutedRpc.instance == null) { return false; } OutboundChunkMessage message = preparedChunk.Message; ZPackage val = new ZPackage(); val.Write(message.DomainKey); val.Write(message.Hash); val.Write(message.TransferKind); val.Write(message.BaseHash); val.Write(message.ChunkIndex); val.Write(message.ChunkCount); val.Write(message.CompressedSize); WriteChunkBytes(val, preparedChunk.SourceBytes, preparedChunk.ChunkOffset, preparedChunk.ChunkLength); val.Write(message.RequestId); ZRoutedRpc.instance.InvokeRoutedRPC(message.Sender, "DropNSpawn Payload Chunk", new object[1] { val }); return true; } private static void WriteChunkBytes(ZPackage package, byte[] sourceBytes, int offset, int count) { if (count <= 0) { package.Write(Array.Empty()); } else if (ZPackageWriterField?.GetValue(package) is BinaryWriter binaryWriter) { package.Write(count); binaryWriter.Write(sourceBytes, offset, count); } else { byte[] array = new byte[count]; Buffer.BlockCopy(sourceBytes, offset, array, 0, count); package.Write(array); } } private static void RPC_ReceivePayloadChunk(long sender, ZPackage package) { if (DropNSpawnPlugin.IsSourceOfTruth || !IsServerRoutedSender(sender)) { return; } string key = package.ReadString(); string hash = package.ReadString(); int transferKind = package.ReadInt(); string baseHash = package.ReadString(); int chunkIndex = package.ReadInt(); int chunkCount = package.ReadInt(); int compressedSize = package.ReadInt(); byte[] chunkBytes = package.ReadByteArray(); long requestId = package.ReadLong(); lock (Sync) { EnsureRpcRegisteredLocked(); if (TransportsByDomainKey.TryGetValue(key, out IDomainTransport value)) { value.ReceivePayloadChunk(sender, hash, requestId, transferKind, baseHash, chunkIndex, chunkCount, compressedSize, chunkBytes); } } } private static bool TryGetCurrentServerRoutedSender(out long sender) { sender = 0L; if (ZRoutedRpc.instance == null || (Object)(object)ZNet.instance == (Object)null) { return false; } if (ZNet.instance.IsServer()) { sender = RoutedRpcIdRef.Invoke(ZRoutedRpc.instance); return sender != 0; } ZNetPeer serverPeer = ZNet.instance.GetServerPeer(); if (serverPeer == null) { return false; } sender = serverPeer.m_uid; return sender != 0; } private static bool IsServerRoutedSender(long sender) { if (TryGetCurrentServerRoutedSender(out var sender2)) { return sender2 == sender; } return false; } private static EntryTransportSchema CreateObjectEntrySchema() { return new EntryTransportSchema(ObjectDropManager.TransportMetadata.DtoVersion, () => new PrefabConfigurationEntry(), StringField((PrefabConfigurationEntry entry) => entry.RuleId, delegate(PrefabConfigurationEntry entry, string value) { entry.RuleId = value; }, (EntrySignatureContext context) => context.IncludeRuleId), StringField((PrefabConfigurationEntry entry) => entry.Prefab, delegate(PrefabConfigurationEntry entry, string value) { entry.Prefab = value; }), BoolField((PrefabConfigurationEntry entry) => entry.Enabled, delegate(PrefabConfigurationEntry entry, bool value) { entry.Enabled = value; }), OptionalField((PrefabConfigurationEntry entry) => entry.Conditions, delegate(PrefabConfigurationEntry entry, ConditionsDefinition? value) { entry.Conditions = value; }, ConditionsValueCodec), OptionalField((PrefabConfigurationEntry entry) => entry.DropOnDestroyed, delegate(PrefabConfigurationEntry entry, DropTableDefinition? value) { entry.DropOnDestroyed = value; }, DropTableValueCodec), OptionalField((PrefabConfigurationEntry entry) => entry.MineRock, delegate(PrefabConfigurationEntry entry, DamageableDropTableDefinition? value) { entry.MineRock = value; }, DamageableDropTableValueCodec), OptionalField((PrefabConfigurationEntry entry) => entry.MineRock5, delegate(PrefabConfigurationEntry entry, DamageableDropTableDefinition? value) { entry.MineRock5 = value; }, DamageableDropTableValueCodec), OptionalField((PrefabConfigurationEntry entry) => entry.TreeBase, delegate(PrefabConfigurationEntry entry, DamageableDropTableDefinition? value) { entry.TreeBase = value; }, DamageableDropTableValueCodec), OptionalField((PrefabConfigurationEntry entry) => entry.TreeLog, delegate(PrefabConfigurationEntry entry, DamageableDropTableDefinition? value) { entry.TreeLog = value; }, DamageableDropTableValueCodec), OptionalField((PrefabConfigurationEntry entry) => entry.Container, delegate(PrefabConfigurationEntry entry, DropTableDefinition? value) { entry.Container = value; }, DropTableValueCodec), OptionalField((PrefabConfigurationEntry entry) => entry.Pickable, delegate(PrefabConfigurationEntry entry, PickableDefinition? value) { entry.Pickable = value; }, PickableValueCodec), OptionalField((PrefabConfigurationEntry entry) => entry.PickableItem, delegate(PrefabConfigurationEntry entry, PickableItemDefinition? value) { entry.PickableItem = value; }, PickableItemValueCodec), OptionalField((PrefabConfigurationEntry entry) => entry.Fish, delegate(PrefabConfigurationEntry entry, FishDefinition? value) { entry.Fish = value; }, FishValueCodec), OptionalField((PrefabConfigurationEntry entry) => entry.Destructible, delegate(PrefabConfigurationEntry entry, DestructibleDefinition? value) { entry.Destructible = value; }, DestructibleValueCodec), CopyOnlyField(delegate(PrefabConfigurationEntry source, PrefabConfigurationEntry target) { target.SourcePath = source.SourcePath; }), CopyOnlyField(delegate(PrefabConfigurationEntry source, PrefabConfigurationEntry target) { target.SourceLine = source.SourceLine; }), CopyOnlyField(delegate(PrefabConfigurationEntry source, PrefabConfigurationEntry target) { target.SourceColumn = source.SourceColumn; })); } private static EntryTransportSchema CreateCharacterEntrySchema() { return new EntryTransportSchema(CharacterDropManager.TransportMetadata.DtoVersion, () => new CharacterDropPrefabEntry(), StringField((CharacterDropPrefabEntry entry) => entry.RuleId, delegate(CharacterDropPrefabEntry entry, string value) { entry.RuleId = value; }, (EntrySignatureContext context) => context.IncludeRuleId), StringField((CharacterDropPrefabEntry entry) => entry.Prefab, delegate(CharacterDropPrefabEntry entry, string value) { entry.Prefab = value; }), BoolField((CharacterDropPrefabEntry entry) => entry.Enabled, delegate(CharacterDropPrefabEntry entry, bool value) { entry.Enabled = value; }), OptionalField((CharacterDropPrefabEntry entry) => entry.Conditions, delegate(CharacterDropPrefabEntry entry, ConditionsDefinition? value) { entry.Conditions = value; }, ConditionsValueCodec), OptionalField((CharacterDropPrefabEntry entry) => entry.CharacterDrop, delegate(CharacterDropPrefabEntry entry, CharacterDropDefinition? value) { entry.CharacterDrop = value; }, CharacterDropValueCodec), CopyOnlyField(delegate(CharacterDropPrefabEntry source, CharacterDropPrefabEntry target) { target.SourcePath = source.SourcePath; }), CopyOnlyField(delegate(CharacterDropPrefabEntry source, CharacterDropPrefabEntry target) { target.SourceLine = source.SourceLine; }), CopyOnlyField(delegate(CharacterDropPrefabEntry source, CharacterDropPrefabEntry target) { target.SourceColumn = source.SourceColumn; })); } private static EntryTransportSchema CreateSpawnerEntrySchema() { return new EntryTransportSchema(SpawnerManager.TransportMetadata.DtoVersion, () => new SpawnerConfigurationEntry(), StringField((SpawnerConfigurationEntry entry) => entry.RuleId, delegate(SpawnerConfigurationEntry entry, string value) { entry.RuleId = value; }, (EntrySignatureContext context) => context.IncludeRuleId), StringField((SpawnerConfigurationEntry entry) => entry.Prefab, delegate(SpawnerConfigurationEntry entry, string value) { entry.Prefab = value; }), BoolField((SpawnerConfigurationEntry entry) => entry.Enabled, delegate(SpawnerConfigurationEntry entry, bool value) { entry.Enabled = value; }), ListField((SpawnerConfigurationEntry entry) => entry.Locations, delegate(SpawnerConfigurationEntry entry, List? value) { entry.Locations = value; }, delegate(PayloadSignatureBuilder builder, string value, EntrySignatureContext _) { builder.WriteString(value ?? ""); }, delegate(ZPackage package, string value) { package.Write(value ?? ""); }, (ZPackage package) => package.ReadString(), (string value) => value ?? ""), OptionalField((SpawnerConfigurationEntry entry) => entry.Conditions, delegate(SpawnerConfigurationEntry entry, ConditionsDefinition? value) { entry.Conditions = value; }, ConditionsValueCodec), OptionalField((SpawnerConfigurationEntry entry) => entry.SpawnArea, delegate(SpawnerConfigurationEntry entry, SpawnAreaDefinition? value) { entry.SpawnArea = value; }, SpawnAreaValueCodec), OptionalField((SpawnerConfigurationEntry entry) => entry.CreatureSpawner, delegate(SpawnerConfigurationEntry entry, CreatureSpawnerDefinition? value) { entry.CreatureSpawner = value; }, CreatureSpawnerValueCodec), CopyOnlyField(delegate(SpawnerConfigurationEntry source, SpawnerConfigurationEntry target) { target.SourcePath = source.SourcePath; }), CopyOnlyField(delegate(SpawnerConfigurationEntry source, SpawnerConfigurationEntry target) { target.SourceLine = source.SourceLine; }), CopyOnlyField(delegate(SpawnerConfigurationEntry source, SpawnerConfigurationEntry target) { target.SourceColumn = source.SourceColumn; })); } private static EntryTransportSchema CreateSpawnSystemEntrySchema() { return new EntryTransportSchema(SpawnSystemManager.TransportMetadata.DtoVersion, () => new CanonicalSpawnSystemEntry(), StringField((CanonicalSpawnSystemEntry entry) => entry.RuleId, delegate(CanonicalSpawnSystemEntry entry, string value) { entry.RuleId = value; }, (EntrySignatureContext context) => context.IncludeRuleId), StringField((CanonicalSpawnSystemEntry entry) => entry.Prefab, delegate(CanonicalSpawnSystemEntry entry, string value) { entry.Prefab = value; }), BoolField((CanonicalSpawnSystemEntry entry) => entry.Enabled, delegate(CanonicalSpawnSystemEntry entry, bool value) { entry.Enabled = value; }), OptionalField((CanonicalSpawnSystemEntry entry) => entry.SpawnSystem, delegate(CanonicalSpawnSystemEntry entry, SpawnSystemSpawnDefinition? value) { entry.SpawnSystem = value; }, SpawnSystemSpawnValueCodec), CopyOnlyField(delegate(CanonicalSpawnSystemEntry source, CanonicalSpawnSystemEntry target) { target.SourcePath = source.SourcePath; }), CopyOnlyField(delegate(CanonicalSpawnSystemEntry source, CanonicalSpawnSystemEntry target) { target.SourceLine = source.SourceLine; }), CopyOnlyField(delegate(CanonicalSpawnSystemEntry source, CanonicalSpawnSystemEntry target) { target.SourceColumn = source.SourceColumn; }), CopyOnlyField(delegate(CanonicalSpawnSystemEntry source, CanonicalSpawnSystemEntry target) { target.ReferenceOwnerName = source.ReferenceOwnerName; })); } private static EntryTransportSchema CreateEventEntrySchema() { return new EntryTransportSchema(EventManager.TransportMetadata.DtoVersion, () => new EventDefinition(), StringField((EventDefinition entry) => entry.Event, delegate(EventDefinition entry, string value) { entry.Event = value; }), ListField((EventDefinition entry) => entry.Settings, delegate(EventDefinition entry, List? value) { entry.Settings = value; }, delegate(PayloadSignatureBuilder builder, string value, EntrySignatureContext _) { builder.WriteString(value ?? ""); }, delegate(ZPackage package, string value) { package.Write(value ?? ""); }, (ZPackage package) => package.ReadString(), (string value) => value ?? ""), ListField((EventDefinition entry) => entry.Standalone, delegate(EventDefinition entry, List? value) { entry.Standalone = value; }, delegate(PayloadSignatureBuilder builder, float value, EntrySignatureContext _) { builder.WriteFloat(value); }, delegate(ZPackage package, float value) { package.Write(value); }, (ZPackage package) => package.ReadSingle(), (float value) => value), NullableFloatField((EventDefinition entry) => entry.SpawnerDelay, delegate(EventDefinition entry, float? value) { entry.SpawnerDelay = value; }), OptionalField((EventDefinition entry) => entry.Conditions, delegate(EventDefinition entry, EventConditionsDefinition? value) { entry.Conditions = value; }, EventConditionsValueCodec), ListField((EventDefinition entry) => entry.Messages, delegate(EventDefinition entry, List? value) { entry.Messages = value; }, delegate(PayloadSignatureBuilder builder, string value, EntrySignatureContext _) { builder.WriteString(value ?? ""); }, delegate(ZPackage package, string value) { package.Write(value ?? ""); }, (ZPackage package) => package.ReadString(), (string value) => value ?? ""), NullableStringField((EventDefinition entry) => entry.ForceEnvironment, delegate(EventDefinition entry, string? value) { entry.ForceEnvironment = value; }), NullableStringField((EventDefinition entry) => entry.ForceMusic, delegate(EventDefinition entry, string? value) { entry.ForceMusic = value; }), ListField((EventDefinition entry) => entry.StartCommands, delegate(EventDefinition entry, List? value) { entry.StartCommands = value; }, delegate(PayloadSignatureBuilder builder, string value, EntrySignatureContext _) { builder.WriteString(value ?? ""); }, delegate(ZPackage package, string value) { package.Write(value ?? ""); }, (ZPackage package) => package.ReadString(), (string value) => value ?? ""), ListField((EventDefinition entry) => entry.EndCommands, delegate(EventDefinition entry, List? value) { entry.EndCommands = value; }, delegate(PayloadSignatureBuilder builder, string value, EntrySignatureContext _) { builder.WriteString(value ?? ""); }, delegate(ZPackage package, string value) { package.Write(value ?? ""); }, (ZPackage package) => package.ReadString(), (string value) => value ?? ""), ListField((EventDefinition entry) => entry.Spawns, delegate(EventDefinition entry, List? value) { entry.Spawns = value; }, EventSpawnValueCodec)); } private static void WriteSpawnSystemSpawnDefinition(ZPackage package, SpawnSystemSpawnDefinition definition) { WriteNullableString(package, definition.Name); WriteNullableBool(package, definition.HuntPlayer); WriteOptional(package, definition.Level, WriteIntRangeDefinition); WriteNullableFloat(package, definition.OverrideLevelUpChance); WriteNullableFloat(package, definition.LevelUpMinCenterDistance); WriteNullableFloat(package, definition.GroundOffset); WriteNullableFloat(package, definition.GroundOffsetRandom); WriteNullableFloat(package, definition.SpawnInterval); WriteNullableFloat(package, definition.SpawnChance); WriteOptional(package, definition.SpawnRadius, WriteFloatRangeDefinition); WriteOptional(package, definition.GroupSize, WriteIntRangeDefinition); WriteNullableFloat(package, definition.GroupRadius); WriteNullableFloat(package, definition.NoSpawnRadius); WriteNullableInt(package, definition.MaxSpawned); WriteOptional(package, definition.Tilt, WriteFloatRangeDefinition); WriteOptional(package, definition.Altitude, WriteFloatRangeDefinition); WriteOptional(package, definition.OceanDepth, WriteFloatRangeDefinition); WriteOptional(package, definition.DistanceFromCenter, WriteFloatRangeDefinition); WriteStringList(package, definition.Biomes); WriteNullableInt(package, GetResolvedBiomeMaskValue(definition.Biomes, definition.ResolvedBiomeMask)); WriteStringList(package, definition.BiomeAreas); WriteOptional(package, definition.TimeOfDay, WriteTimeOfDayDefinition); WriteStringList(package, definition.RequiredEnvironments); WriteNullableString(package, definition.RequiredGlobalKey); WriteNullableBool(package, definition.InLava); WriteNullableBool(package, definition.InForest); WriteNullableBool(package, definition.InsidePlayerBase); WriteNullableBool(package, definition.CanSpawnCloseToPlayer); WriteStringDictionary(package, definition.Fields); WriteStringList(package, definition.Objects); WriteNullableString(package, definition.Data); WriteNullableString(package, definition.Faction); } private static SpawnSystemSpawnDefinition ReadSpawnSystemSpawnDefinition(ZPackage package) { SpawnSystemSpawnDefinition obj = new SpawnSystemSpawnDefinition { Name = ReadNullableString(package), HuntPlayer = ReadNullableBool(package), Level = ReadOptional(package, ReadIntRangeDefinition), OverrideLevelUpChance = ReadNullableFloat(package), LevelUpMinCenterDistance = ReadNullableFloat(package), GroundOffset = ReadNullableFloat(package), GroundOffsetRandom = ReadNullableFloat(package), SpawnInterval = ReadNullableFloat(package), SpawnChance = ReadNullableFloat(package), SpawnRadius = ReadOptional(package, ReadFloatRangeDefinition), GroupSize = ReadOptional(package, ReadIntRangeDefinition), GroupRadius = ReadNullableFloat(package), NoSpawnRadius = ReadNullableFloat(package), MaxSpawned = ReadNullableInt(package), Tilt = ReadOptional(package, ReadFloatRangeDefinition), Altitude = ReadOptional(package, ReadFloatRangeDefinition), OceanDepth = ReadOptional(package, ReadFloatRangeDefinition), DistanceFromCenter = ReadOptional(package, ReadFloatRangeDefinition), Biomes = ReadStringList(package) }; int? num = ReadNullableInt(package); Biome? resolvedBiomeMask; if (num.HasValue) { int valueOrDefault = num.GetValueOrDefault(); resolvedBiomeMask = (Biome)valueOrDefault; } else { resolvedBiomeMask = null; } obj.ResolvedBiomeMask = resolvedBiomeMask; obj.BiomeAreas = ReadStringList(package); obj.TimeOfDay = ReadOptional(package, ReadTimeOfDayDefinition); obj.RequiredEnvironments = ReadStringList(package); obj.RequiredGlobalKey = ReadNullableString(package); obj.InLava = ReadNullableBool(package); obj.InForest = ReadNullableBool(package); obj.InsidePlayerBase = ReadNullableBool(package); obj.CanSpawnCloseToPlayer = ReadNullableBool(package); obj.Fields = ReadStringDictionary(package); obj.Objects = ReadStringList(package); obj.Data = ReadNullableString(package); obj.Faction = ReadNullableString(package); return obj; } private static string ComputeObjectPayloadSignature(List entries) { return ObjectCodec.Schema.ComputePayloadSignature(entries); } internal static string ComputeObjectConfigurationSignature(IEnumerable? entries) { return ComputeObjectPayloadSignature((entries ?? Enumerable.Empty()).ToList()); } internal static string ComputeObjectEntryIdentitySignature(PrefabConfigurationEntry entry) { return ObjectCodec.Schema.ComputeEntrySignature(entry, includeRuleId: false, includeResolvedBiomeMask: false); } internal static string ComputeObjectDropRowSignature(DropEntryDefinition definition) { PayloadSignatureBuilder payloadSignatureBuilder = new PayloadSignatureBuilder(); WriteDropEntryDefinition(payloadSignatureBuilder, definition); return payloadSignatureBuilder.ComputeHash(); } private static string ComputeCharacterPayloadSignature(List entries) { return CharacterCodec.Schema.ComputePayloadSignature(entries); } internal static string ComputeCharacterConfigurationSignature(IEnumerable? entries) { return ComputeCharacterPayloadSignature((entries ?? Enumerable.Empty()).ToList()); } internal static string ComputeCharacterEntryIdentitySignature(CharacterDropPrefabEntry entry) { return CharacterCodec.Schema.ComputeEntrySignature(entry, includeRuleId: false, includeResolvedBiomeMask: false); } internal static string ComputeCharacterDropRowSignature(CharacterDropEntryDefinition definition) { PayloadSignatureBuilder payloadSignatureBuilder = new PayloadSignatureBuilder(); WriteCharacterDropEntryDefinition(payloadSignatureBuilder, definition); return payloadSignatureBuilder.ComputeHash(); } private static string ComputeSpawnerPayloadSignature(List entries) { return SpawnerCodec.Schema.ComputePayloadSignature(entries); } internal static string ComputeSpawnerConfigurationSignature(IEnumerable? entries) { return ComputeSpawnerPayloadSignature((entries ?? Enumerable.Empty()).ToList()); } internal static string ComputeSpawnerEntryIdentitySignature(SpawnerConfigurationEntry entry) { return SpawnerCodec.Schema.ComputeEntrySignature(entry, includeRuleId: false, includeResolvedBiomeMask: false); } private static string ComputeSpawnSystemPayloadSignature(IReadOnlyList entries) { return SpawnSystemCodec.Schema.ComputePayloadSignature(entries); } internal static string ComputeSpawnSystemProjectedConfigurationSignature(IReadOnlyList entries, Func selector) { return SpawnSystemCodec.Schema.ComputePayloadSignature(entries, selector); } internal static string ComputeSpawnSystemConfigurationSignature(IEnumerable? entries) { if (entries == null) { return ComputeSpawnSystemPayloadSignature(Array.Empty()); } if (entries is IReadOnlyList entries2) { return ComputeSpawnSystemPayloadSignature(entries2); } return ComputeSpawnSystemPayloadSignature(entries.ToList()); } internal static string ComputeSpawnSystemEntrySignature(CanonicalSpawnSystemEntry entry) { return SpawnSystemCodec.Schema.ComputeEntrySignature(entry, includeRuleId: false, includeResolvedBiomeMask: true); } internal static string ComputeSpawnSystemEntryIdentitySignature(CanonicalSpawnSystemEntry entry) { return SpawnSystemCodec.Schema.ComputeEntrySignature(entry, includeRuleId: false, includeResolvedBiomeMask: false); } private static void WriteSpawnSystemSpawnDefinition(PayloadSignatureBuilder builder, SpawnSystemSpawnDefinition definition, bool includeResolvedBiomeMask) { WriteNullableString(builder, definition.Name); WriteNullableBool(builder, definition.HuntPlayer); WriteOptional(builder, definition.Level, WriteIntRangeDefinition); WriteNullableFloat(builder, definition.OverrideLevelUpChance); WriteNullableFloat(builder, definition.LevelUpMinCenterDistance); WriteNullableFloat(builder, definition.GroundOffset); WriteNullableFloat(builder, definition.GroundOffsetRandom); WriteNullableFloat(builder, definition.SpawnInterval); WriteNullableFloat(builder, definition.SpawnChance); WriteOptional(builder, definition.SpawnRadius, WriteFloatRangeDefinition); WriteOptional(builder, definition.GroupSize, WriteIntRangeDefinition); WriteNullableFloat(builder, definition.GroupRadius); WriteNullableFloat(builder, definition.NoSpawnRadius); WriteNullableInt(builder, definition.MaxSpawned); WriteOptional(builder, definition.Tilt, WriteFloatRangeDefinition); WriteOptional(builder, definition.Altitude, WriteFloatRangeDefinition); WriteOptional(builder, definition.OceanDepth, WriteFloatRangeDefinition); WriteOptional(builder, definition.DistanceFromCenter, WriteFloatRangeDefinition); WriteStringList(builder, definition.Biomes); if (includeResolvedBiomeMask) { WriteNullableInt(builder, GetResolvedBiomeMaskValue(definition.Biomes, definition.ResolvedBiomeMask)); } WriteStringList(builder, definition.BiomeAreas); WriteOptional(builder, definition.TimeOfDay, WriteTimeOfDayDefinition); WriteStringList(builder, definition.RequiredEnvironments); WriteNullableString(builder, definition.RequiredGlobalKey); WriteNullableBool(builder, definition.InLava); WriteNullableBool(builder, definition.InForest); WriteNullableBool(builder, definition.InsidePlayerBase); WriteNullableBool(builder, definition.CanSpawnCloseToPlayer); WriteStringDictionary(builder, definition.Fields); WriteStringList(builder, definition.Objects); WriteNullableString(builder, definition.Data); WriteNullableString(builder, definition.Faction); } } internal sealed class PrefabConfigurationEntry { [YamlMember(Order = 1)] public string Prefab { get; set; } = ""; [YamlMember(Order = 2)] public bool Enabled { get; set; } = true; [YamlMember(Order = 3)] public ConditionsDefinition? Conditions { get; set; } [YamlMember(Order = 4)] public DropTableDefinition? DropOnDestroyed { get; set; } [YamlMember(Order = 5)] public DamageableDropTableDefinition? MineRock { get; set; } [YamlMember(Order = 6)] public DamageableDropTableDefinition? MineRock5 { get; set; } [YamlMember(Order = 7)] public DamageableDropTableDefinition? TreeBase { get; set; } [YamlMember(Order = 8)] public DamageableDropTableDefinition? TreeLog { get; set; } [YamlMember(Order = 9)] public DropTableDefinition? Container { get; set; } [YamlMember(Order = 10)] public PickableItemDefinition? PickableItem { get; set; } [YamlMember(Order = 11)] public PickableDefinition? Pickable { get; set; } [YamlMember(Order = 12)] public FishDefinition? Fish { get; set; } [YamlMember(Order = 13)] public DestructibleDefinition? Destructible { get; set; } [YamlIgnore] public string RuleId { get; set; } = ""; [YamlIgnore] public string? SourcePath { get; set; } [YamlIgnore] public int SourceLine { get; set; } [YamlIgnore] public int SourceColumn { get; set; } } internal sealed class PrefabReferenceEntry { [YamlMember(Order = 1)] public string Prefab { get; set; } = ""; [YamlMember(Order = 2)] public DropTableDefinition? DropOnDestroyed { get; set; } [YamlMember(Order = 3)] public DamageableDropTableDefinition? MineRock { get; set; } [YamlMember(Order = 4)] public DamageableDropTableDefinition? MineRock5 { get; set; } [YamlMember(Order = 5)] public DamageableDropTableDefinition? TreeBase { get; set; } [YamlMember(Order = 6)] public DamageableDropTableDefinition? TreeLog { get; set; } [YamlMember(Order = 7)] public DropTableDefinition? Container { get; set; } [YamlMember(Order = 8)] public PickableItemDefinition? PickableItem { get; set; } [YamlMember(Order = 9)] public PickableDefinition? Pickable { get; set; } [YamlMember(Order = 10)] public FishDefinition? Fish { get; set; } [YamlMember(Order = 11)] public DestructibleDefinition? Destructible { get; set; } } internal sealed class ObjectLocationReferenceEntry { [YamlMember(Order = 1)] public string Prefab { get; set; } = ""; [YamlMember(Order = 2)] public List Components { get; set; } = new List(); [YamlMember(Order = 3)] public List Locations { get; set; } = new List(); } internal class DropTablePayloadDefinition { [YamlMember(Order = 10)] public IntRangeDefinition? Rolls { get; set; } [YamlIgnore] public int? DropMin { get; set; } [YamlIgnore] public int? DropMax { get; set; } [YamlMember(Order = 11)] public float? DropChance { get; set; } [YamlMember(Order = 12)] public bool? OneOfEach { get; set; } [YamlMember(Order = 13)] public List? Drops { get; set; } } internal class DropTableDefinition : DropTablePayloadDefinition { } internal sealed class DamageableDropTableDefinition : DropTableDefinition { [YamlMember(Order = 1)] public float? Health { get; set; } [YamlMember(Order = 2)] public int? MinToolTier { get; set; } } internal sealed class DestructibleDefinition { [YamlMember(Order = 1)] public float? Health { get; set; } [YamlMember(Order = 2)] public int? MinToolTier { get; set; } [YamlMember(Order = 3)] public string? DestructibleType { get; set; } [YamlMember(Order = 4)] public string? SpawnWhenDestroyed { get; set; } } internal sealed class DropEntryDefinition { [YamlMember(Order = 1)] public string Item { get; set; } = ""; [YamlMember(Order = 2)] public IntRangeDefinition? Stack { get; set; } [YamlIgnore] public int? StackMin { get; set; } [YamlIgnore] public int? StackMax { get; set; } [YamlMember(Order = 3)] public float? Weight { get; set; } [YamlMember(Order = 4)] public bool? DontScale { get; set; } } internal sealed class PickableDefinition { [YamlMember(Order = 1)] public string? OverrideName { get; set; } [YamlMember(Order = 2)] public PickableDropDefinition? Drop { get; set; } [YamlMember(Order = 3)] public DropTablePayloadDefinition? ExtraDrops { get; set; } } internal sealed class PickableDropDefinition { [YamlMember(Order = 1)] public string Item { get; set; } = ""; [YamlMember(Order = 2)] public int? Amount { get; set; } [YamlMember(Order = 3)] public int? MinAmountScaled { get; set; } [YamlMember(Order = 4)] public bool? DontScale { get; set; } } internal sealed class PickableItemDefinition { [YamlMember(Order = 1)] public List? RandomDrops { get; set; } [YamlMember(Order = 2)] public PickableItemDropDefinition? Drop { get; set; } } internal sealed class PickableItemDropDefinition { [YamlMember(Order = 1)] public string Item { get; set; } = ""; [YamlMember(Order = 2)] public int? Stack { get; set; } } internal sealed class RandomPickableItemDefinition { [YamlMember(Order = 1)] public string Item { get; set; } = ""; [YamlMember(Order = 2)] public IntRangeDefinition? Stack { get; set; } [YamlIgnore] public int? StackMin { get; set; } [YamlIgnore] public int? StackMax { get; set; } [YamlMember(Order = 3)] public float? Weight { get; set; } } internal sealed class FishDefinition { [YamlMember(Order = 1)] public DropTablePayloadDefinition? ExtraDrops { get; set; } } internal static class ObjectDropManager { private readonly struct PendingObjectReconcileGroup { public string GroupKey { get; } public int Epoch { get; } public PendingObjectReconcileGroup(string groupKey, int epoch) { GroupKey = groupKey; Epoch = epoch; } } private readonly struct PendingObjectReconcileItem { public GameObject GameObject { get; } public int InstanceId { get; } public PendingObjectReconcileItem(GameObject gameObject, int instanceId) { GameObject = gameObject; InstanceId = instanceId; } } private sealed class PendingObjectReconcileGroupState { public RingBufferQueue Items { get; } = new RingBufferQueue(); public HashSet InstanceIds { get; } = new HashSet(); public LiveObjectComponentKind ComponentKinds { get; set; } public bool HighPriority { get; set; } public bool IsQueued { get; set; } } private sealed class GroupConditionalApplyPlan { public HashSet EligibleEntries { get; } = new HashSet(); public List MatchingEntries { get; } = new List(); public HashSet EligibleCompiledRules { get; } = new HashSet(); public List MatchingCompiledRules { get; } = new List(); } private sealed class GroupConditionalApplyPlanCacheEntry { public GroupConditionalApplyPlan Plan { get; set; } public LinkedListNode LruNode { get; set; } } [Flags] internal enum LiveObjectComponentKind { None = 0, DropOnDestroyed = 1, MineRock = 2, MineRock5 = 4, TreeBase = 8, TreeLog = 0x10, Container = 0x20, Pickable = 0x40, PickableItem = 0x80, Fish = 0x100, Destructible = 0x200, Piece = 0x400 } private sealed class PickableSnapshot { public GameObject? ItemPrefab { get; set; } public int Amount { get; set; } public int MinAmountScaled { get; set; } public bool DontScale { get; set; } public string OverrideName { get; set; } = ""; public DropTable ExtraDrops { get; set; } = new DropTable(); } private sealed class PickableItemRandomSnapshot { public GameObject? ItemPrefab { get; set; } public int StackMin { get; set; } public int StackMax { get; set; } } private sealed class PickableItemSnapshot { public GameObject? ItemPrefab { get; set; } public int Stack { get; set; } public List RandomItems { get; set; } = new List(); } private sealed class BuiltRandomPickableItems { public RandomItem[] Items { get; set; } = Array.Empty(); public float[] Weights { get; set; } = Array.Empty(); } private sealed class WeightedRandomPickableItemState { public float[] Weights { get; set; } = Array.Empty(); } private sealed class FishSnapshot { public DropTable ExtraDrops { get; set; } = new DropTable(); } private sealed class DestructibleSnapshot { public DestructibleType DestructibleType { get; set; } public GameObject? SpawnWhenDestroyed { get; set; } } private sealed class HealthSnapshot { public float? Destructible { get; set; } public float? MineRock { get; set; } public float? MineRock5 { get; set; } public float? TreeBase { get; set; } public float? TreeLog { get; set; } } private sealed class MinToolTierSnapshot { public int? Destructible { get; set; } public int? MineRock { get; set; } public int? MineRock5 { get; set; } public int? TreeBase { get; set; } public int? TreeLog { get; set; } } private sealed class PrefabSnapshot { public GameObject Prefab { get; set; } public HealthSnapshot? Health { get; set; } public MinToolTierSnapshot? MinToolTier { get; set; } public DropTable? DropOnDestroyed { get; set; } public DropTable? MineRock { get; set; } public DropTable? MineRock5 { get; set; } public DropTable? TreeBase { get; set; } public DropTable? TreeLog { get; set; } public DropTable? Container { get; set; } public PickableSnapshot? Pickable { get; set; } public PickableItemSnapshot? PickableItem { get; set; } public FishSnapshot? Fish { get; set; } public DestructibleSnapshot? Destructible { get; set; } } private sealed class SyncedObjectConfigurationState { public List Configuration { get; set; } = new List(); public Dictionary> ActiveEntriesByPrefab { get; } = new Dictionary>(StringComparer.OrdinalIgnoreCase); public Dictionary ConfiguredComponentKindsByPrefab { get; } = new Dictionary(StringComparer.OrdinalIgnoreCase); public Dictionary ReconcileComponentKindsByPrefab { get; } = new Dictionary(StringComparer.OrdinalIgnoreCase); public Dictionary> VneiEntriesByPrefab { get; } = new Dictionary>(StringComparer.OrdinalIgnoreCase); public string ConfigurationSignature { get; set; } = ""; } private sealed class CompiledDropTableRow { public string Fingerprint { get; set; } = ""; public GameObject ItemPrefab { get; set; } public int StackMin { get; set; } public int StackMax { get; set; } public float Weight { get; set; } public bool DontScale { get; set; } } private sealed class CompiledDropTablePayload { public bool HasDropRangeOverride { get; set; } public int DropMin { get; set; } public int DropMax { get; set; } public bool HasDropChanceOverride { get; set; } public float DropChance { get; set; } public bool HasOneOfEachOverride { get; set; } public bool OneOfEach { get; set; } public List Drops { get; } = new List(); } private sealed class CompiledPickableDefinition { public bool HasItemPrefabOverride { get; set; } public GameObject? ItemPrefab { get; set; } public bool HasAmountOverride { get; set; } public int Amount { get; set; } public bool HasMinAmountScaledOverride { get; set; } public int MinAmountScaled { get; set; } public bool HasDontScaleOverride { get; set; } public bool DontScale { get; set; } public bool HasOverrideNameOverride { get; set; } public string OverrideName { get; set; } = ""; } private sealed class CompiledPickableItemDefinition { public bool HasRandomOverride { get; set; } public RandomItem[] RandomItems { get; set; } = Array.Empty(); public float[] RandomWeights { get; set; } = Array.Empty(); public bool HasFixedDrop { get; set; } public bool HasFixedItemOverride { get; set; } public ItemDrop? FixedItemPrefab { get; set; } public bool HasFixedStackOverride { get; set; } public int FixedStack { get; set; } } private sealed class CompiledDamageableScalarDefinition { public bool HasHealthOverride { get; set; } public float Health { get; set; } public bool HasMinToolTierOverride { get; set; } public int MinToolTier { get; set; } } private sealed class CompiledDestructibleComponentDefinition { public bool HasHealthOverride { get; set; } public float Health { get; set; } public bool HasMinToolTierOverride { get; set; } public int MinToolTier { get; set; } public bool HasDestructibleTypeOverride { get; set; } public DestructibleType DestructibleType { get; set; } public bool HasSpawnWhenDestroyedOverride { get; set; } public GameObject? SpawnWhenDestroyed { get; set; } } private sealed class CompiledObjectDropRule { public PrefabConfigurationEntry Entry { get; set; } public bool HasConditions { get; set; } public CompiledDropTablePayload? DropOnDestroyed { get; set; } public CompiledDropTablePayload? MineRock { get; set; } public CompiledDropTablePayload? MineRock5 { get; set; } public CompiledDropTablePayload? TreeBase { get; set; } public CompiledDropTablePayload? TreeLog { get; set; } public CompiledDropTablePayload? Container { get; set; } public CompiledDropTablePayload? PickableExtraDrops { get; set; } public CompiledDropTablePayload? FishExtraDrops { get; set; } public CompiledPickableDefinition? Pickable { get; set; } public CompiledPickableItemDefinition? PickableItem { get; set; } public CompiledDestructibleComponentDefinition? Destructible { get; set; } public CompiledDamageableScalarDefinition? MineRockScalars { get; set; } public CompiledDamageableScalarDefinition? MineRock5Scalars { get; set; } public CompiledDamageableScalarDefinition? TreeBaseScalars { get; set; } public CompiledDamageableScalarDefinition? TreeLogScalars { get; set; } } private sealed class StaticCompiledDropTableTemplate { public DropTable Template { get; set; } public HashSet Fingerprints { get; } = new HashSet(StringComparer.Ordinal); } private sealed class CompiledObjectPrefabPlan { public List ActiveEntries { get; } = new List(); public List Rules { get; } = new List(); public Dictionary StaticDropTableTemplates { get; } = new Dictionary(); } private sealed class ObjectRuntimeDropConfigurationState { public static ObjectRuntimeDropConfigurationState Empty { get; } = new ObjectRuntimeDropConfigurationState(); public Dictionary PlansByPrefab { get; } = new Dictionary(StringComparer.OrdinalIgnoreCase); public Dictionary RulesByEntry { get; } = new Dictionary(); } private sealed class PendingSnapshotBuildState { public int GameDataSignature { get; set; } public int SnapshotSignature { get; set; } public string Source { get; set; } = ""; public List Prefabs { get; } = new List(); public List Snapshots { get; } = new List(); public Dictionary SnapshotsByPrefab { get; } = new Dictionary(StringComparer.OrdinalIgnoreCase); public int NextIndex { get; set; } } private sealed class LocationReferenceBucket { public SortedSet Components { get; } = new SortedSet(StringComparer.OrdinalIgnoreCase); public SortedSet Locations { get; } = new SortedSet(StringComparer.OrdinalIgnoreCase); public Dictionary ReferenceEntriesBySignature { get; } = new Dictionary(StringComparer.Ordinal); } private sealed class ObjectApplyOperations : StandardBaselineDesiredStateOperations { public static ObjectApplyOperations Instance { get; } = new ObjectApplyOperations(); public override string DomainKey => "object"; public override BaselineDesiredStateCapabilities Capabilities => BaselineDesiredStateCapabilities.Validation | BaselineDesiredStateCapabilities.StaticBaseline | BaselineDesiredStateCapabilities.StaticApply | BaselineDesiredStateCapabilities.LiveApply | BaselineDesiredStateCapabilities.StaticRollback; public override void Validate(ObjectDesiredState desiredState) { ValidateObjectDesiredState(desiredState); } public override void RestoreStaticBaseline(ObjectDesiredState desiredState) { RestoreObjectStaticBaseline(desiredState); } public override void ApplyDesiredStateToStaticBaseline(ObjectDesiredState desiredState) { ApplyObjectDesiredStateToStaticBaseline(desiredState); } public override void ApplyDesiredStateToLive(ObjectDesiredState desiredState) { ApplyObjectDesiredStateToLive(desiredState); } public override void Commit(ObjectDesiredState desiredState) { RecordAppliedState(desiredState.GameDataSignature, desiredState.DomainEnabled, desiredState.CurrentEntrySignatures); } public override void HandleFailure(ObjectDesiredState desiredState, bool liveStageFailed) { if (liveStageFailed) { QueueReapplyActiveEntriesToLiveObjects(desiredState.LiveDirtyPrefabs); } } } private sealed class ObjectDesiredState { public StandardDomainApplyPlan ApplyPlan { get; set; } public int GameDataSignature { get; set; } public Dictionary CurrentEntrySignatures { get; set; } = EmptyEntrySignatures; public ObjectRuntimeDropConfigurationState RuntimeConfigurationState { get; set; } = ObjectRuntimeDropConfigurationState.Empty; public bool DomainEnabled { get; set; } public bool QueueLiveReconcile { get; set; } public HashSet? DirtyPrefabs { get; set; } public HashSet? LiveDirtyPrefabs { get; set; } public bool NeedsLiveReload { get; set; } } private sealed class ObjectConditionPlanCacheState { private readonly Dictionary _groupConditionalApplyPlans = new Dictionary(StringComparer.Ordinal); private readonly LinkedList _groupConditionalApplyPlanLru = new LinkedList(); private readonly Dictionary _staticConditionContexts = new Dictionary(); public bool TryGetGroupConditionalApplyPlan(string cacheKey, out GroupConditionalApplyPlan? plan) { if (_groupConditionalApplyPlans.TryGetValue(cacheKey, out GroupConditionalApplyPlanCacheEntry value)) { TouchGroupConditionalApplyPlanCacheEntry(value); plan = value.Plan; return true; } plan = null; return false; } public void StoreGroupConditionalApplyPlan(string cacheKey, GroupConditionalApplyPlan plan) { if (_groupConditionalApplyPlans.TryGetValue(cacheKey, out GroupConditionalApplyPlanCacheEntry value)) { value.Plan = plan; TouchGroupConditionalApplyPlanCacheEntry(value); return; } LinkedListNode lruNode = _groupConditionalApplyPlanLru.AddLast(cacheKey); _groupConditionalApplyPlans[cacheKey] = new GroupConditionalApplyPlanCacheEntry { Plan = plan, LruNode = lruNode }; TrimGroupConditionalApplyPlanCacheIfNeeded(); } public void InvalidateStaticConditionContextForInstance(int instanceId) { _staticConditionContexts.Remove(instanceId); } public bool TryGetStaticConditionContext(int instanceId, Vector3 position, out StaticConditionContextSnapshot snapshot) { //IL_0011: 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 (_staticConditionContexts.TryGetValue(instanceId, out snapshot) && snapshot.Position == position) { return true; } snapshot = null; return false; } public StaticConditionContextSnapshot StoreStaticConditionContext(int instanceId, StaticConditionContextSnapshot snapshot) { _staticConditionContexts[instanceId] = snapshot; return snapshot; } public void Clear() { _groupConditionalApplyPlans.Clear(); _groupConditionalApplyPlanLru.Clear(); _staticConditionContexts.Clear(); } private void TouchGroupConditionalApplyPlanCacheEntry(GroupConditionalApplyPlanCacheEntry cacheEntry) { if (cacheEntry.LruNode.List != null && cacheEntry.LruNode != _groupConditionalApplyPlanLru.Last) { _groupConditionalApplyPlanLru.Remove(cacheEntry.LruNode); _groupConditionalApplyPlanLru.AddLast(cacheEntry.LruNode); } } private void TrimGroupConditionalApplyPlanCacheIfNeeded() { if (_groupConditionalApplyPlans.Count > 2048) { while (_groupConditionalApplyPlans.Count > 1536 && _groupConditionalApplyPlanLru.First != null) { LinkedListNode? first = _groupConditionalApplyPlanLru.First; _groupConditionalApplyPlanLru.RemoveFirst(); string value = first.Value; _groupConditionalApplyPlans.Remove(value); } } } } private sealed class StaticConditionContextSnapshot { public Vector3 Position { get; set; } public string ResolvedLocationName { get; set; } = ""; public Biome Biome { get; set; } public bool InDungeon { get; set; } } private sealed class EventDropTableStateSet { private readonly Dictionary _states = new Dictionary(); public CachedEventDropTableState GetOrCreate(LiveObjectComponentKind componentKind) { if (_states.TryGetValue(componentKind, out CachedEventDropTableState value)) { return value; } CachedEventDropTableState cachedEventDropTableState = new CachedEventDropTableState(); _states[componentKind] = cachedEventDropTableState; return cachedEventDropTableState; } } private sealed class CachedEventDropTableState { public int Epoch { get; private set; } = -1; public string PrefabName { get; private set; } = ""; public bool IsInitialized { get; set; } public DropTable? SnapshotTable { get; private set; } public CompiledObjectDropRule[] RelevantRules { get; set; } = Array.Empty(); public bool HasCustomBlock { get; set; } public bool UsesTimeOfDay { get; set; } public bool UsesRequiredEnvironments { get; set; } public bool UsesInsidePlayerBase { get; set; } public string[] RequiredGlobalKeys { get; set; } = Array.Empty(); public string[] ForbiddenGlobalKeys { get; set; } = Array.Empty(); public int LastRuntimeSignature { get; set; } = int.MinValue; public bool HasCachedResolution { get; set; } public bool LastHasOverride { get; set; } public DropTable? LastResolvedDropTable { get; set; } public float LastInsidePlayerBaseSampleTime { get; set; } = float.NegativeInfinity; public bool IsInsidePlayerBase { get; set; } public void Reset(int epoch, string prefabName, DropTable? snapshotTable) { Epoch = epoch; PrefabName = prefabName ?? ""; SnapshotTable = snapshotTable; IsInitialized = false; RelevantRules = Array.Empty(); HasCustomBlock = false; UsesTimeOfDay = false; UsesRequiredEnvironments = false; UsesInsidePlayerBase = false; RequiredGlobalKeys = Array.Empty(); ForbiddenGlobalKeys = Array.Empty(); LastRuntimeSignature = int.MinValue; HasCachedResolution = false; LastHasOverride = false; LastResolvedDropTable = null; LastInsidePlayerBaseSampleTime = float.NegativeInfinity; IsInsidePlayerBase = false; } } private sealed class EventDropRuntimeContextSnapshot { public int Frame { get; set; } public int TimeOfDayPhaseMarker { get; set; } public string EnvironmentName { get; set; } = ""; public Dictionary GlobalKeyStates { get; } = new Dictionary(StringComparer.Ordinal); } private sealed class ObjectLazyRuntimeState { private readonly ConditionalWeakTable _pickableItemRandomStates = new ConditionalWeakTable(); private readonly ConditionalWeakTable _eventDropTableStates = new ConditionalWeakTable(); private EventDropRuntimeContextSnapshot? _eventDropRuntimeContextSnapshot; public bool HasRandomState(PickableItem pickableItem) { WeightedRandomPickableItemState value; return _pickableItemRandomStates.TryGetValue(pickableItem, out value); } public bool TryGetRandomState(PickableItem pickableItem, out WeightedRandomPickableItemState state) { return _pickableItemRandomStates.TryGetValue(pickableItem, out state); } public void SetRandomWeights(PickableItem pickableItem, float[] weights) { _pickableItemRandomStates.Remove(pickableItem); if (weights.Length != 0) { _pickableItemRandomStates.Add(pickableItem, new WeightedRandomPickableItemState { Weights = weights }); } } public void ClearRandomState(PickableItem pickableItem) { _pickableItemRandomStates.Remove(pickableItem); } public CachedEventDropTableState GetOrCreateEventDropTableState(GameObject gameObject, LiveObjectComponentKind componentKind) { return _eventDropTableStates.GetOrCreateValue(gameObject).GetOrCreate(componentKind); } public EventDropRuntimeContextSnapshot GetOrCreateEventDropRuntimeContextSnapshot() { int frameCount = Time.frameCount; if (_eventDropRuntimeContextSnapshot != null && _eventDropRuntimeContextSnapshot.Frame == frameCount) { return _eventDropRuntimeContextSnapshot; } EventDropRuntimeContextSnapshot obj = new EventDropRuntimeContextSnapshot { Frame = frameCount, TimeOfDayPhaseMarker = TimeOfDayFormatting.GetCurrentRuntimePhaseMarker() }; EnvMan instance = EnvMan.instance; obj.EnvironmentName = ((instance == null) ? null : instance.GetCurrentEnvironment()?.m_name) ?? ""; _eventDropRuntimeContextSnapshot = obj; return _eventDropRuntimeContextSnapshot; } } private sealed class ObjectLiveRegistryState { private readonly Dictionary> _liveObjectsByPrefab = new Dictionary>(StringComparer.OrdinalIgnoreCase); private readonly Dictionary _liveObjectPrefabsByInstance = new Dictionary(); private readonly HashSet _bootstrappedTrackedPrefabs = new HashSet(StringComparer.OrdinalIgnoreCase); private int? _liveObjectRegistrySceneInstanceId; public bool HasSceneSession(int currentSceneInstanceId) { return _liveObjectRegistrySceneInstanceId == currentSceneInstanceId; } public void BeginSceneSession(int currentSceneInstanceId) { _liveObjectRegistrySceneInstanceId = currentSceneInstanceId; ClearTracked(); } public HashSet CollectUnbootstrappedTrackedPrefabs(IEnumerable prefabNames) { HashSet hashSet = new HashSet(StringComparer.OrdinalIgnoreCase); foreach (string prefabName in prefabNames) { if (!string.IsNullOrWhiteSpace(prefabName) && !_bootstrappedTrackedPrefabs.Contains(prefabName)) { hashSet.Add(prefabName); } } return hashSet; } public void MarkTrackedPrefabsBootstrapped(IEnumerable prefabNames) { foreach (string prefabName in prefabNames) { if (!string.IsNullOrWhiteSpace(prefabName)) { _bootstrappedTrackedPrefabs.Add(prefabName); } } } public IEnumerable EnumerateRegisteredLiveObjects(HashSet prefabNames) { foreach (string prefabName in prefabNames) { if (!_liveObjectsByPrefab.TryGetValue(prefabName, out HashSet value)) { continue; } foreach (GameObject item in value) { if ((Object)(object)item != (Object)null) { yield return item; } } } } public bool TryGetTrackedPrefab(GameObject gameObject, out string prefabName) { return _liveObjectPrefabsByInstance.TryGetValue(gameObject, out prefabName); } public void Register(GameObject gameObject, string prefabName) { if (_liveObjectPrefabsByInstance.TryGetValue(gameObject, out string value)) { if (string.Equals(value, prefabName, StringComparison.OrdinalIgnoreCase)) { return; } Unregister(gameObject, value); } _liveObjectPrefabsByInstance[gameObject] = prefabName; if (!_liveObjectsByPrefab.TryGetValue(prefabName, out HashSet value2)) { value2 = new HashSet(); _liveObjectsByPrefab[prefabName] = value2; } value2.Add(gameObject); } public void Unregister(GameObject gameObject, string prefabName) { _liveObjectPrefabsByInstance.Remove(gameObject); if (_liveObjectsByPrefab.TryGetValue(prefabName, out HashSet value)) { value.Remove(gameObject); if (value.Count == 0) { _liveObjectsByPrefab.Remove(prefabName); } } } public void CollectDeadObjects(List> target) { target.Clear(); foreach (KeyValuePair item in _liveObjectPrefabsByInstance) { if ((Object)(object)item.Key == (Object)null) { target.Add(item); } } } public void ClearTracked() { _liveObjectsByPrefab.Clear(); _liveObjectPrefabsByInstance.Clear(); _bootstrappedTrackedPrefabs.Clear(); } } private sealed class ObjectPrefabProfileCatalogState { private readonly Dictionary _configuredComponentKindsByPrefab = new Dictionary(StringComparer.OrdinalIgnoreCase); private readonly Dictionary _reconcileComponentKindsByPrefab = new Dictionary(StringComparer.OrdinalIgnoreCase); private readonly Dictionary _lastAppliedConfiguredComponentKindsByPrefab = new Dictionary(StringComparer.OrdinalIgnoreCase); private readonly Dictionary _lastAppliedReconcileComponentKindsByPrefab = new Dictionary(StringComparer.OrdinalIgnoreCase); public void Clear() { ClearCurrentProfiles(); _lastAppliedConfiguredComponentKindsByPrefab.Clear(); _lastAppliedReconcileComponentKindsByPrefab.Clear(); } public void ClearCurrentProfiles() { _configuredComponentKindsByPrefab.Clear(); _reconcileComponentKindsByPrefab.Clear(); } public void RefreshConfiguredPrefabProfile(Dictionary> activeEntriesByPrefab, string prefabName) { ObjectDropManager.RefreshConfiguredPrefabProfile(activeEntriesByPrefab, _configuredComponentKindsByPrefab, prefabName); RefreshReconcilePrefabProfile(activeEntriesByPrefab, _reconcileComponentKindsByPrefab, prefabName); } public void ApplySyncedProfiles(Dictionary configuredComponentKindsByPrefab, Dictionary reconcileComponentKindsByPrefab) { ReplaceComponentKinds(_configuredComponentKindsByPrefab, configuredComponentKindsByPrefab); ReplaceComponentKinds(_reconcileComponentKindsByPrefab, reconcileComponentKindsByPrefab); } public bool TryGetReconcileKinds(string prefabName, out LiveObjectComponentKind reconcileKinds) { return _reconcileComponentKindsByPrefab.TryGetValue(prefabName, out reconcileKinds); } public bool RequiresLiveReconcile(string prefabName) { if (prefabName.Length == 0) { return false; } if (_reconcileComponentKindsByPrefab.TryGetValue(prefabName, out var value) && value != LiveObjectComponentKind.None) { return true; } if (_lastAppliedReconcileComponentKindsByPrefab.TryGetValue(prefabName, out var value2)) { return value2 != LiveObjectComponentKind.None; } return false; } public bool RequiresLiveObjectTracking(string prefabName) { if (prefabName.Length == 0) { return false; } if (_configuredComponentKindsByPrefab.TryGetValue(prefabName, out var value) && value != LiveObjectComponentKind.None) { return true; } if (_lastAppliedConfiguredComponentKindsByPrefab.TryGetValue(prefabName, out var value2)) { return value2 != LiveObjectComponentKind.None; } return false; } public bool RequiresLiveReconcile(string prefabName, LiveObjectComponentKind componentKind) { if (componentKind == LiveObjectComponentKind.Piece) { return RequiresLiveReconcile(prefabName); } if (_reconcileComponentKindsByPrefab.TryGetValue(prefabName, out var value)) { return (value & componentKind) != 0; } return false; } public HashSet FilterPrefabsRequiringLiveReconcile(IEnumerable? prefabNames) { HashSet hashSet = new HashSet(StringComparer.OrdinalIgnoreCase); if (prefabNames == null) { return hashSet; } foreach (string prefabName in prefabNames) { if (RequiresLiveReconcile(prefabName)) { hashSet.Add(prefabName); } } return hashSet; } public HashSet FilterPrefabsRequiringLiveTracking(IEnumerable? prefabNames) { HashSet hashSet = new HashSet(StringComparer.OrdinalIgnoreCase); if (prefabNames == null) { return hashSet; } foreach (string prefabName in prefabNames) { if (RequiresLiveObjectTracking(prefabName)) { hashSet.Add(prefabName); } } return hashSet; } public HashSet SnapshotPrefabsRequiringLiveTracking() { HashSet hashSet = new HashSet(StringComparer.OrdinalIgnoreCase); string key; LiveObjectComponentKind value; foreach (KeyValuePair item3 in _configuredComponentKindsByPrefab) { item3.Deconstruct(out key, out value); string item = key; if (value != LiveObjectComponentKind.None) { hashSet.Add(item); } } foreach (KeyValuePair item4 in _lastAppliedConfiguredComponentKindsByPrefab) { item4.Deconstruct(out key, out value); string item2 = key; if (value != LiveObjectComponentKind.None) { hashSet.Add(item2); } } return hashSet; } public void RecordCurrentConfiguredKindsAsLastApplied() { ReplaceComponentKinds(_lastAppliedConfiguredComponentKindsByPrefab, _configuredComponentKindsByPrefab); } public void RecordCurrentReconcileKindsAsLastApplied() { ReplaceComponentKinds(_lastAppliedReconcileComponentKindsByPrefab, _reconcileComponentKindsByPrefab); } public void ClearLastAppliedConfiguredKinds() { _lastAppliedConfiguredComponentKindsByPrefab.Clear(); } public void ClearLastAppliedReconcileKinds() { _lastAppliedReconcileComponentKindsByPrefab.Clear(); } } private sealed class ObjectReconcileQueueState { private readonly RingBufferQueue _pendingHighPriorityObjectReconcileGroups = new RingBufferQueue(); private readonly RingBufferQueue _pendingLowPriorityObjectReconcileGroups = new RingBufferQueue(); private readonly Dictionary _pendingObjectReconcileGroups = new Dictionary(StringComparer.Ordinal); private readonly HashSet _pendingObjectReconcileInstanceIds = new HashSet(); public bool HasPendingWork() { if (_pendingHighPriorityObjectReconcileGroups.Count <= 0) { return _pendingLowPriorityObjectReconcileGroups.Count > 0; } return true; } public bool HasPendingGroups(bool highPriorityOnly) { if (_pendingHighPriorityObjectReconcileGroups.Count <= 0) { if (!highPriorityOnly) { return _pendingLowPriorityObjectReconcileGroups.Count > 0; } return false; } return true; } public bool TryGetGroupState(string groupKey, out PendingObjectReconcileGroupState groupState) { return _pendingObjectReconcileGroups.TryGetValue(groupKey, out groupState); } public void RemoveGroup(string groupKey) { _pendingObjectReconcileGroups.Remove(groupKey); } public IEnumerable> EnumerateGroups() { return _pendingObjectReconcileGroups; } public void EnqueueGroup(PendingObjectReconcileGroup queuedGroup, bool highPriority) { if (highPriority) { _pendingHighPriorityObjectReconcileGroups.Enqueue(queuedGroup); } else { _pendingLowPriorityObjectReconcileGroups.Enqueue(queuedGroup); } } public bool TryDequeueGroup(out PendingObjectReconcileGroup queuedGroup, bool highPriorityOnly = false) { if (_pendingHighPriorityObjectReconcileGroups.TryDequeue(out queuedGroup)) { return true; } if (highPriorityOnly) { return false; } return _pendingLowPriorityObjectReconcileGroups.TryDequeue(out queuedGroup); } public bool TryAddPendingInstance(int instanceId) { return _pendingObjectReconcileInstanceIds.Add(instanceId); } public bool TryTakePendingInstance(int instanceId) { return _pendingObjectReconcileInstanceIds.Remove(instanceId); } public void RemovePendingState(int instanceId) { _pendingObjectReconcileInstanceIds.Remove(instanceId); } public PendingObjectReconcileGroupState GetOrCreateGroup(string groupKey, LiveObjectComponentKind configuredKinds, bool highPriority) { if (_pendingObjectReconcileGroups.TryGetValue(groupKey, out PendingObjectReconcileGroupState value)) { return value; } value = new PendingObjectReconcileGroupState { ComponentKinds = configuredKinds, HighPriority = highPriority }; _pendingObjectReconcileGroups[groupKey] = value; return value; } public void Clear() { _pendingHighPriorityObjectReconcileGroups.Clear(); _pendingLowPriorityObjectReconcileGroups.Clear(); _pendingObjectReconcileGroups.Clear(); _pendingObjectReconcileInstanceIds.Clear(); } } private sealed class ObjectConfigurationRuntimeState { public List Configuration { get; set; } = new List(); public string ConfigurationSignature { get; set; } = ""; public Dictionary> ActiveEntriesByPrefab { get; } = new Dictionary>(StringComparer.OrdinalIgnoreCase); public Dictionary> VneiEntriesByPrefab { get; } = new Dictionary>(StringComparer.OrdinalIgnoreCase); public void Reset() { Configuration = new List(); ConfigurationSignature = ""; ActiveEntriesByPrefab.Clear(); VneiEntriesByPrefab.Clear(); } } private sealed class ObjectSnapshotRuntimeState { public List Snapshots { get; } = new List(); public Dictionary SnapshotsByPrefab { get; } = new Dictionary(StringComparer.OrdinalIgnoreCase); public void Clear() { Snapshots.Clear(); SnapshotsByPrefab.Clear(); } public void ReplaceWith(PendingSnapshotBuildState buildState) { Snapshots.Clear(); Snapshots.AddRange(buildState.Snapshots); SnapshotsByPrefab.Clear(); foreach (var (key, value) in buildState.SnapshotsByPrefab) { SnapshotsByPrefab[key] = value; } } } private sealed class ObjectRuntimeDropState { public ObjectRuntimeDropConfigurationState Configuration { get; set; } = ObjectRuntimeDropConfigurationState.Empty; public string ConfigurationSignature { get; set; } = ""; public int? GameDataSignature { get; set; } public void Reset() { Configuration = ObjectRuntimeDropConfigurationState.Empty; ConfigurationSignature = ""; GameDataSignature = null; } } private const string ReferenceAutoUpdateStateKey = "object"; private const string LocationReferenceAutoUpdateStateKey = "object.locations"; internal static readonly DomainModuleDefinition Module = new DomainModuleDefinition(new DomainModuleOptions { DomainKey = "object", ReloadDomain = DropNSpawnPlugin.ReloadDomain.Object, ManifestSettingKey = "object_yaml", ManifestPriority = 100, ShouldReloadForPath = ShouldReloadForPath, Reload = ReloadConfiguration, InitializeRuntime = Initialize, OnGameDataReady = OnGameDataReady, HandleExpandWorldDataReady = HandleExpandWorldDataReady, DtoVersion = 7, TransportProfile = DomainTransportProfile.LargeConfig, DisplayName = "object", CacheDirectoryName = "object", ClientRequestPriority = 100, KeySelector = (PrefabConfigurationEntry entry) => entry.RuleId, ApplyPayloadAction = ApplySyncedPayload, HasPendingSnapshotBuildWork = HasPendingSnapshotBuildWork, ProcessPendingSnapshotBuildStep = ProcessPendingSnapshotBuildStep, HasPendingReconcileWork = HasPendingReconcileWork, ProcessPendingReconcileStep = ProcessQueuedReconcileStep, BeforeClientManifestChanged = MarkSyncedPayloadPending, OnClientAuthorityCutover = EnterPendingSyncedPayloadState }); private static readonly object Sync = new object(); private static readonly IDeserializer Deserializer = new DeserializerBuilder().WithNamingConvention(CamelCaseNamingConvention.Instance).Build(); private static readonly ISerializer Serializer = new SerializerBuilder().WithNamingConvention(CamelCaseNamingConvention.Instance).ConfigureDefaultValuesHandling(DefaultValuesHandling.OmitNull | DefaultValuesHandling.OmitDefaults).Build(); private static readonly ObjectSnapshotRuntimeState SnapshotState = new ObjectSnapshotRuntimeState(); private static readonly ObjectConfigurationRuntimeState RuntimeState = new ObjectConfigurationRuntimeState(); private static readonly HashSet MissingComponentWarnings = new HashSet(StringComparer.OrdinalIgnoreCase); private static readonly InvalidEntryDiagnostics InvalidEntryWarnings = new InvalidEntryDiagnostics(); private static readonly int DestructibleLazyScalarSignatureKey = StringExtensionMethods.GetStableHashCode("DropNSpawn.destructible_scalar_signature"); private static readonly int MineRockLazyScalarSignatureKey = StringExtensionMethods.GetStableHashCode("DropNSpawn.minerock_scalar_signature"); private static readonly int MineRock5LazyScalarSignatureKey = StringExtensionMethods.GetStableHashCode("DropNSpawn.minerock5_scalar_signature"); private static readonly int TreeBaseLazyScalarSignatureKey = StringExtensionMethods.GetStableHashCode("DropNSpawn.treebase_scalar_signature"); private static readonly int TreeLogLazyScalarSignatureKey = StringExtensionMethods.GetStableHashCode("DropNSpawn.treelog_scalar_signature"); private static readonly MethodInfo? PickableItemSetupItemMethod = AccessTools.Method(typeof(PickableItem), "SetupItem", (Type[])null, (Type[])null); private static readonly FieldInfo? MineRock5HitAreasField = AccessTools.Field(typeof(MineRock5), "m_hitAreas"); private static readonly MethodInfo? MineRock5SaveHealthMethod = AccessTools.Method(typeof(MineRock5), "SaveHealth", (Type[])null, (Type[])null); private static readonly MethodInfo? MineRock5UpdateMeshMethod = AccessTools.Method(typeof(MineRock5), "UpdateMesh", (Type[])null, (Type[])null); private static readonly FieldInfo? MineRock5HitAreaHealthField = AccessTools.Field(typeof(MineRock5).GetNestedType("HitArea", BindingFlags.NonPublic), "m_health"); private static bool _initialized; private static int? _lastProcessedSnapshotSignature; private static int? _lastProcessedGameDataSignature; private static readonly ObjectRuntimeDropState DropRuntimeState = new ObjectRuntimeDropState(); private static int _cachedGameDataSignatureFrame = -1; private static int _cachedGameDataSignatureValue; private static int _cachedSnapshotSignatureFrame = -1; private static int _cachedSnapshotSignatureValue; private static bool _referenceArtifactsAutoRefreshConsumed; private static readonly Dictionary _lastAppliedEntrySignaturesByPrefab = new Dictionary(StringComparer.OrdinalIgnoreCase); private static string _lastAppliedConfigurationSignature = ""; private static int? _lastAppliedGameDataSignature; private static bool? _lastAppliedDomainEnabled; private static bool _lastAppliedSynchronizedPayloadReady; private static bool _synchronizedPayloadReady; private static int? _lastCommittedAuthorityEpoch; private static int _reconcileQueueEpoch; private static PendingSnapshotBuildState? _pendingSnapshotBuild; private const string MockPrefabPrefix = "JVLmock_"; private const int GroupConditionalApplyPlanCacheLimit = 2048; private const int GroupConditionalApplyPlanCacheTrimTarget = 1536; private static readonly DomainConfigurationRuntime ConfigurationRuntime = new DomainConfigurationRuntime(new DomainLoadHooks(ParseLocalConfigurationDocuments, BuildSyncedConfigurationState, CommitSyncedConfigurationState, RejectLocalConfigurationPayload, (SyncedObjectConfigurationState state) => state.Configuration.Count, LogPartiallyAcceptedLocalConfigurationHook, LogLocalConfigurationLoaded, OnSourceOfTruthPayloadUnchanged, delegate { ConfigurationDomainHost.PublishSyncedPayload(DropNSpawnPlugin.IsSourceOfTruth, Descriptor, RuntimeState.Configuration, RuntimeState.ConfigurationSignature); }), new DomainSyncHooks(delegate(out List configuration, out string payloadToken) { return ConfigurationDomainHost.TryGetSyncedEntries(Descriptor, out configuration, out payloadToken); }, (string payloadToken) => ConfigurationDomainHost.ShouldSkipSyncedPayload(LoadState, payloadToken, Volatile.Read(in _synchronizedPayloadReady)), BuildSyncedConfigurationState, CommitSyncedConfigurationState, (SyncedObjectConfigurationState state) => state.ActiveEntriesByPrefab.Count, "ServerSync:DropNSpawnObject", MarkSyncedPayloadPending, LogSyncedObjectConfigurationLoaded, LogSyncedObjectConfigurationFailure)); private static readonly Dictionary EmptyEntrySignatures = new Dictionary(StringComparer.OrdinalIgnoreCase); private static readonly ObjectConditionPlanCacheState ConditionPlanCacheState = new ObjectConditionPlanCacheState(); private static readonly ObjectLazyRuntimeState LazyRuntimeState = new ObjectLazyRuntimeState(); private static readonly ObjectLiveRegistryState LiveRegistryState = new ObjectLiveRegistryState(); private static readonly ObjectPrefabProfileCatalogState PrefabProfileCatalogState = new ObjectPrefabProfileCatalogState(); private static readonly ObjectReconcileQueueState ReconcileQueueState = new ObjectReconcileQueueState(); internal static DomainDescriptor Descriptor => Module.DescriptorTyped; internal static DomainTransportMetadata TransportMetadata => Module.TransportMetadataTyped; private static DomainLoadState LoadState => ConfigurationRuntime.LoadState; private static string ReferenceConfigurationPath => Path.Combine(DropNSpawnPlugin.YamlConfigDirectoryPath, PluginSettingsFacade.GetYamlDomainFilePrefix("object") + ".reference.yml"); private static string LocationReferenceConfigurationPath => Path.Combine(DropNSpawnPlugin.YamlConfigDirectoryPath, PluginSettingsFacade.GetYamlDomainFilePrefix("object") + ".locations.reference.yml"); private static string PrimaryOverrideConfigurationPathYml => Path.Combine(DropNSpawnPlugin.YamlConfigDirectoryPath, PluginSettingsFacade.GetYamlDomainFilePrefix("object") + ".yml"); private static string PrimaryOverrideConfigurationPathYaml => Path.Combine(DropNSpawnPlugin.YamlConfigDirectoryPath, PluginSettingsFacade.GetYamlDomainFilePrefix("object") + ".yaml"); private static string FullScaffoldConfigurationPath => Path.Combine(DropNSpawnPlugin.YamlConfigDirectoryPath, PluginSettingsFacade.GetYamlDomainFilePrefix("object") + ".full.yml"); internal static string RulesWatcherPattern => DropNSpawnPlugin.YamlRulesWatcherPattern; private static void ApplyDesiredStateToPrefabs(ObjectDesiredState desiredState) { if (desiredState.DomainEnabled) { ApplyConfigurationToPrefabs(desiredState, desiredState.DirtyPrefabs); } } private static void ApplyDesiredStateToLiveObjects(ObjectDesiredState desiredState) { if (!desiredState.NeedsLiveReload) { return; } if (desiredState.ApplyPlan.SameGameData && desiredState.LiveDirtyPrefabs != null) { if (desiredState.LiveDirtyPrefabs.Count != 0) { if (desiredState.QueueLiveReconcile) { QueueReapplyActiveEntriesToLiveObjects(desiredState.LiveDirtyPrefabs); } else { ReapplyActiveEntriesToLiveObjects(desiredState, desiredState.DomainEnabled, desiredState.LiveDirtyPrefabs); } } } else if (desiredState.QueueLiveReconcile) { QueueReapplyActiveEntriesToLiveObjects(); } else { ReapplyActiveEntriesToLiveObjects(desiredState, desiredState.DomainEnabled); } } private static void ApplyConfigurationToPrefabs(ObjectDesiredState desiredState, HashSet? targetPrefabs = null) { IReadOnlyDictionary plansByPrefab = desiredState.RuntimeConfigurationState.PlansByPrefab; object obj; if (targetPrefabs == null) { obj = plansByPrefab.Keys; } else { obj = targetPrefabs; } foreach (string item in (IEnumerable)obj) { if (!plansByPrefab.TryGetValue(item, out var value)) { continue; } List activeEntries = value.ActiveEntries; if (!SnapshotState.SnapshotsByPrefab.TryGetValue(item, out PrefabSnapshot value2)) { foreach (PrefabConfigurationEntry item2 in activeEntries) { WarnInvalidEntry("Object prefab '" + item + "' from " + DescribeEntrySource(item2) + " was not found in ZNetScene."); } continue; } foreach (PrefabConfigurationEntry item3 in activeEntries) { ApplyConfiguredComponents(value2.Prefab, value2, item3, updateRuntimeState: false, allowConditionalMatches: false); } ApplyEffectiveDropTableOverrides(value2.Prefab, value2, activeEntries, allowConditionalMatches: false); } } private static void ReapplyActiveEntriesToLiveObjects(ObjectDesiredState desiredState, bool domainEnabled, HashSet? dirtyPrefabs = null) { IReadOnlyDictionary plansByPrefab = desiredState.RuntimeConfigurationState.PlansByPrefab; foreach (GameObject item in (dirtyPrefabs == null) ? GetRegisteredLiveObjects() : GetRegisteredLiveObjects(dirtyPrefabs)) { string prefabName = GetPrefabName(item); if (!RequiresLiveReconcileForPrefab(prefabName)) { continue; } RegisterLiveObject(item, prefabName); if (SnapshotState.SnapshotsByPrefab.TryGetValue(prefabName, out PrefabSnapshot value)) { RestoreConfiguredComponents(item, value, CreateRestoreMask(value), updateRuntimeState: true); if (domainEnabled && plansByPrefab.TryGetValue(prefabName, out var value2)) { ReconcileConfiguredInstance(item, value, value2.ActiveEntries); } } } } private static void QueueReapplyActiveEntriesToLiveObjects(HashSet? dirtyPrefabs = null) { foreach (GameObject item in (dirtyPrefabs == null) ? GetRegisteredLiveObjects() : GetRegisteredLiveObjects(dirtyPrefabs)) { string prefabName = GetPrefabName(item); if (prefabName.Length != 0 && RequiresLiveReconcileForPrefab(prefabName)) { RegisterLiveObject(item, prefabName); if (SnapshotState.SnapshotsByPrefab.ContainsKey(prefabName)) { QueueTrackedObjectInstanceReconcileLocked(item, prefabName); } } } } internal static bool ShouldReloadForPath(string? path) { if (PluginSettingsFacade.IsEligibleOverrideConfigurationPath(path)) { return IsOverrideConfigurationFileName(Path.GetFileName(path ?? "")); } return false; } internal static void Initialize() { lock (Sync) { if (!_initialized) { LoadConfiguration(); _initialized = true; } } } internal static void ReloadConfiguration() { lock (Sync) { LoadConfiguration(); ApplyIfReady(queueLiveReconcile: true); PromoteQueuedReconcileGroupsLocked(LiveObjectComponentKind.TreeBase | LiveObjectComponentKind.TreeLog); DrainQueuedHighPriorityReconcilesLocked(); } } internal static void MarkSyncedPayloadPending() { lock (Sync) { ConfigurationRuntime.MarkSyncedPayloadPending(DropNSpawnPlugin.IsSourceOfTruth, delegate { Volatile.Write(ref _synchronizedPayloadReady, value: false); }); } } internal static void EnterPendingSyncedPayloadState() { lock (Sync) { Dictionary previousVneiEntrySignatures = BuildVneiEntrySignaturesByPrefab(); HashSet previouslyAppliedPrefabs = BuildLastAppliedPrefabs(); ConfigurationRuntime.EnterPendingSyncedPayloadState(DropNSpawnPlugin.IsSourceOfTruth, ResetLoadedConfigurationState, delegate { RuntimeState.ConfigurationSignature = ""; _lastAppliedSynchronizedPayloadReady = false; RestoreSnapshots(previouslyAppliedPrefabs); RestoreTrackedLiveObjects(previouslyAppliedPrefabs); RefreshVneiCompatibility(previousVneiEntrySignatures); }); } } private static bool CanUseCurrentRuntimeState() { if (!DropNSpawnPlugin.IsSourceOfTruth && !Volatile.Read(in _synchronizedPayloadReady)) { return _lastCommittedAuthorityEpoch == NetworkPayloadSyncSupport.CurrentAuthorityEpoch; } return true; } private static HashSet BuildLastAppliedPrefabs() { HashSet hashSet = new HashSet(StringComparer.OrdinalIgnoreCase); if (_lastAppliedDomainEnabled != true) { return hashSet; } foreach (string key in _lastAppliedEntrySignaturesByPrefab.Keys) { hashSet.Add(key); } return hashSet; } internal static bool HandleExpandWorldDataReady() { lock (Sync) { if (!DropNSpawnPlugin.IsSourceOfTruth) { return false; } string text = NetworkPayloadSyncSupport.ComputeObjectConfigurationSignature(RuntimeState.Configuration); if (string.Equals(text, RuntimeState.ConfigurationSignature, StringComparison.Ordinal)) { return false; } RuntimeState.ConfigurationSignature = text; ConfigurationDomainHost.PublishSyncedPayload(DropNSpawnPlugin.IsSourceOfTruth, Descriptor, RuntimeState.Configuration, RuntimeState.ConfigurationSignature); ApplyIfReady(queueLiveReconcile: true); return true; } } internal static void ApplySyncedPayload() { lock (Sync) { Dictionary previousVneiEntrySignatures = BuildVneiEntrySignaturesByPrefab(); ConfigurationRuntime.ApplySyncedPayload(delegate { RefreshVneiCompatibility(previousVneiEntrySignatures); ApplyIfReady(queueLiveReconcile: true); }); } } internal static void OnGameDataReady(string source) { lock (Sync) { if (!_initialized) { Initialize(); } if (!IsGameDataReady()) { return; } int num = ComputeGameDataSignature(); int num2 = ComputeSnapshotSignature(); if (_lastProcessedGameDataSignature != num) { if (_lastProcessedSnapshotSignature != num2 || SnapshotState.Snapshots.Count == 0) { ScheduleSnapshotBuildLocked(source, num, num2); } else { CompleteGameDataReadyLocked(source, num, num2); } } } } internal static bool HasPendingSnapshotBuildWork() { lock (Sync) { return _pendingSnapshotBuild != null; } } internal static bool ProcessPendingSnapshotBuildStep(double deadline) { GameObject val = null; PendingSnapshotBuildState pendingSnapshotBuild; lock (Sync) { pendingSnapshotBuild = _pendingSnapshotBuild; if (pendingSnapshotBuild == null) { return false; } if (pendingSnapshotBuild.NextIndex >= pendingSnapshotBuild.Prefabs.Count) { CompletePendingSnapshotBuildLocked(pendingSnapshotBuild); return true; } val = pendingSnapshotBuild.Prefabs[pendingSnapshotBuild.NextIndex]; pendingSnapshotBuild.NextIndex++; } PrefabSnapshot prefabSnapshot = (((Object)(object)val != (Object)null) ? CaptureSnapshot(val) : null); lock (Sync) { if (_pendingSnapshotBuild == null || pendingSnapshotBuild == null || _pendingSnapshotBuild != pendingSnapshotBuild) { return true; } if (prefabSnapshot != null) { pendingSnapshotBuild.Snapshots.Add(prefabSnapshot); if (!pendingSnapshotBuild.SnapshotsByPrefab.ContainsKey(((Object)prefabSnapshot.Prefab).name)) { pendingSnapshotBuild.SnapshotsByPrefab.Add(((Object)prefabSnapshot.Prefab).name, prefabSnapshot); } } if (pendingSnapshotBuild.NextIndex >= pendingSnapshotBuild.Prefabs.Count && Time.realtimeSinceStartupAsDouble <= deadline) { CompletePendingSnapshotBuildLocked(pendingSnapshotBuild); } } return true; } private static bool IsGameDataReady() { if ((Object)(object)ZNetScene.instance != (Object)null) { return (Object)(object)ObjectDB.instance != (Object)null; } return false; } private static void EnsureReferenceArtifactsUpToDate() { if (!IsGameDataReady()) { return; } string sourceSignature = ComputeReferenceSourceSignature(); ReferenceArtifactUpdateKind updateKind; bool flag = ReferenceArtifactLifecycle.TryPlanUpdate("object", ReferenceConfigurationPath, sourceSignature, out updateKind); ReferenceArtifactUpdateKind updateKind2; bool flag2 = ReferenceArtifactLifecycle.TryPlanUpdate("object.locations", LocationReferenceConfigurationPath, sourceSignature, out updateKind2); if (updateKind == ReferenceArtifactUpdateKind.Created || updateKind2 == ReferenceArtifactUpdateKind.Created) { flag = updateKind == ReferenceArtifactUpdateKind.Created; flag2 = updateKind2 == ReferenceArtifactUpdateKind.Created; } if (flag || flag2) { CaptureSnapshotsIfNeeded(); if (flag) { WriteReferenceConfigurationFile(BuildReferenceConfigurationTemplate(), ReferenceArtifactLifecycle.FormatAction(updateKind) + " object reference configuration at " + ReferenceConfigurationPath + "."); ReferenceArtifactLifecycle.RecordUpdate("object", ReferenceConfigurationPath, sourceSignature); } if (flag2) { WriteLocationReferenceConfigurationFile(BuildLocationReferenceConfigurationTemplate()); DropNSpawnPlugin.DropNSpawnLogger.LogInfo((object)(ReferenceArtifactLifecycle.FormatAction(updateKind2) + " object location reference configuration at " + LocationReferenceConfigurationPath + ".")); ReferenceArtifactLifecycle.RecordUpdate("object.locations", LocationReferenceConfigurationPath, sourceSignature); } } } private static bool EnsurePrimaryOverrideConfigurationFileExists() { if (DomainConfigurationFileSupport.HasAnyOverrideConfigurationFile("object", PrimaryOverrideConfigurationPathYml, PrimaryOverrideConfigurationPathYaml)) { return false; } if (!IsGameDataReady() || SnapshotState.Snapshots.Count == 0) { return false; } GeneratedArtifactWriter.WriteTextAlways(PrimaryOverrideConfigurationPathYml, BuildPrimaryOverrideConfigurationTemplate(), "Created object override configuration at " + PrimaryOverrideConfigurationPathYml + "."); return true; } private static void LoadConfiguration() { Dictionary previousVneiEntrySignatures = BuildVneiEntrySignaturesByPrefab(); if (DropNSpawnPlugin.IsSourceOfTruth) { if (ConfigurationRuntime.ReloadSourceOfTruth(EnumerateOverrideConfigurationPaths().ToList()) == DomainReloadOutcome.Loaded) { RefreshVneiCompatibility(previousVneiEntrySignatures); } } else if (ConfigurationRuntime.ReloadSynced() == DomainReloadOutcome.Loaded) { RefreshVneiCompatibility(previousVneiEntrySignatures); } } private static void ScheduleSnapshotBuildLocked(string source, int gameDataSignature, int snapshotSignature) { if (_pendingSnapshotBuild == null || _pendingSnapshotBuild.GameDataSignature != gameDataSignature || _pendingSnapshotBuild.SnapshotSignature != snapshotSignature) { PendingSnapshotBuildState pendingSnapshotBuildState = new PendingSnapshotBuildState(); pendingSnapshotBuildState.GameDataSignature = gameDataSignature; pendingSnapshotBuildState.SnapshotSignature = snapshotSignature; pendingSnapshotBuildState.Source = source; pendingSnapshotBuildState.Prefabs.AddRange(EnumerateRelevantPrefabs()); _pendingSnapshotBuild = pendingSnapshotBuildState; } } private static void CompletePendingSnapshotBuildLocked(PendingSnapshotBuildState buildState) { if (_pendingSnapshotBuild != null && _pendingSnapshotBuild == buildState) { SnapshotState.ReplaceWith(buildState); _pendingSnapshotBuild = null; CompleteGameDataReadyLocked(buildState.Source, buildState.GameDataSignature, buildState.SnapshotSignature); } } private static void CompleteGameDataReadyLocked(string source, int gameDataSignature, int snapshotSignature) { EnsureLiveObjectRegistrySessionLocked(); ClearQueuedReconcileState(); if (DropNSpawnPlugin.IsSourceOfTruth) { if (!_referenceArtifactsAutoRefreshConsumed) { EnsureReferenceArtifactsUpToDate(); _referenceArtifactsAutoRefreshConsumed = true; } if (EnsurePrimaryOverrideConfigurationFileExists()) { LoadConfiguration(); } } else { _referenceArtifactsAutoRefreshConsumed = true; } ApplyIfReady(queueLiveReconcile: true); _lastProcessedSnapshotSignature = snapshotSignature; _lastProcessedGameDataSignature = gameDataSignature; } private static void ResetLoadedConfigurationState() { ClearQueuedReconcileState(); RuntimeState.Reset(); PrefabProfileCatalogState.ClearCurrentProfiles(); MissingComponentWarnings.Clear(); InvalidEntryWarnings.Clear(); DropRuntimeState.Reset(); Volatile.Write(ref _synchronizedPayloadReady, value: false); } private static List CloneAndNormalizeConfigurationEntries(List? configuration, string sourceName) { List list = NetworkPayloadSyncSupport.CloneEntries(Descriptor, configuration); foreach (PrefabConfigurationEntry item in list) { NormalizeEntry(item); item.SourcePath = (string.IsNullOrWhiteSpace(item.SourcePath) ? sourceName : item.SourcePath); } return list; } private static List PrepareLocalConfigurationEntries(List? configuration, string sourceName, List warnings) { List list = CloneAndNormalizeConfigurationEntries(configuration, sourceName); List list2 = new List(); foreach (PrefabConfigurationEntry item in list) { if (TryAcceptLocalConfigurationEntry(item, warnings)) { list2.Add(item); } } return list2; } private static bool TryAcceptLocalConfigurationEntry(PrefabConfigurationEntry entry, List warnings) { if (!entry.Enabled) { return true; } string text = CreateConfigurationContext(entry); if (string.IsNullOrWhiteSpace(entry.Prefab)) { warnings.Add("Entry '" + text + "' is missing required prefab."); return false; } if (!TryResolveConfiguredObjectPrefab(entry.Prefab, out var hasSupportedObjectComponents)) { warnings.Add("Entry '" + text + "' references unknown object prefab '" + entry.Prefab + "'."); return false; } if (!hasSupportedObjectComponents) { warnings.Add("Entry '" + text + "' references '" + entry.Prefab + "', but it is not a supported object prefab."); return false; } return true; } private static bool TryResolveConfiguredObjectPrefab(string prefabName, out bool hasSupportedObjectComponents) { hasSupportedObjectComponents = true; if ((Object)(object)ZNetScene.instance == (Object)null || string.IsNullOrWhiteSpace(prefabName)) { return true; } GameObject prefab = ZNetScene.instance.GetPrefab(prefabName.Trim()); if ((Object)(object)prefab == (Object)null) { hasSupportedObjectComponents = false; return false; } hasSupportedObjectComponents = HasRelevantLiveObjectComponents(prefab); return true; } private static SyncedObjectConfigurationState BuildSyncedConfigurationState(List configuration, string sourceName) { using (BeginInvalidEntryWarningSuppressionForSyncedClientBuild(sourceName)) { SyncedObjectConfigurationState syncedObjectConfigurationState = new SyncedObjectConfigurationState(); foreach (PrefabConfigurationEntry item in CloneAndNormalizeConfigurationEntries(configuration, sourceName)) { if (!string.IsNullOrWhiteSpace(item.Prefab)) { RemoveEffectiveConfigurationEntry(syncedObjectConfigurationState.Configuration, syncedObjectConfigurationState.ActiveEntriesByPrefab, item.Prefab, item.RuleId); if (item.Enabled) { syncedObjectConfigurationState.Configuration.Add(item); GetOrCreateActiveEntries(syncedObjectConfigurationState.ActiveEntriesByPrefab, item.Prefab).Add(item); } } } RefreshConfiguredPrefabProfiles(syncedObjectConfigurationState.ActiveEntriesByPrefab, syncedObjectConfigurationState.ConfiguredComponentKindsByPrefab, syncedObjectConfigurationState.ReconcileComponentKindsByPrefab); RebuildVneiDisplayEntries(syncedObjectConfigurationState.Configuration, syncedObjectConfigurationState.VneiEntriesByPrefab); syncedObjectConfigurationState.ConfigurationSignature = NetworkPayloadSyncSupport.ComputeObjectConfigurationSignature(syncedObjectConfigurationState.Configuration); return syncedObjectConfigurationState; } } private static void CommitSyncedConfigurationState(SyncedObjectConfigurationState state, string payloadToken) { ResetLoadedConfigurationState(); RuntimeState.Configuration = state.Configuration; string key; List value; foreach (KeyValuePair> item in state.ActiveEntriesByPrefab) { item.Deconstruct(out key, out value); string key2 = key; List value2 = value; RuntimeState.ActiveEntriesByPrefab[key2] = value2; } PrefabProfileCatalogState.ApplySyncedProfiles(state.ConfiguredComponentKindsByPrefab, state.ReconcileComponentKindsByPrefab); foreach (KeyValuePair> item2 in state.VneiEntriesByPrefab) { item2.Deconstruct(out key, out value); string key3 = key; List value3 = value; RuntimeState.VneiEntriesByPrefab[key3] = value3; } RuntimeState.ConfigurationSignature = state.ConfigurationSignature; LoadState.LastLoadedPayload = payloadToken; LoadState.LastRejectedPayload = ""; LoadState.PendingStrictPayload = ""; LoadState.LastRejectedValidationKey = ""; Volatile.Write(ref _synchronizedPayloadReady, value: true); _lastCommittedAuthorityEpoch = (DropNSpawnPlugin.IsSourceOfTruth ? ((int?)null) : new int?(NetworkPayloadSyncSupport.CurrentAuthorityEpoch)); } private static LocalLoadResult ParseLocalConfigurationDocuments(List documents) { return ConfigurationLoadSupport.ParseLocalConfigurationDocuments(documents, ParseConfiguration, PrepareLocalConfigurationEntries, FormatYamlExceptionLocation, "Object override YAML must start with a root list like '- prefab: ...'."); } private static void RejectLocalConfigurationPayload(string payload, IEnumerable errors) { ConfigurationDomainHost.RejectLocalConfigurationPayload(LoadState, payload, errors, "object"); } private static bool RemoveEffectiveConfigurationEntry(string prefabName, string ruleId) { bool result = RemoveEffectiveConfigurationEntry(RuntimeState.Configuration, RuntimeState.ActiveEntriesByPrefab, prefabName, ruleId); RefreshConfiguredPrefabProfile(prefabName); return result; } private static bool RemoveEffectiveConfigurationEntry(List configuration, Dictionary> activeEntriesByPrefab, string prefabName, string ruleId) { bool result = false; for (int num = configuration.Count - 1; num >= 0; num--) { PrefabConfigurationEntry prefabConfigurationEntry = configuration[num]; if (string.Equals(prefabConfigurationEntry.Prefab, prefabName, StringComparison.OrdinalIgnoreCase) && string.Equals(prefabConfigurationEntry.RuleId, ruleId, StringComparison.Ordinal)) { configuration.RemoveAt(num); result = true; } } if (activeEntriesByPrefab.TryGetValue(prefabName, out List value)) { for (int num2 = value.Count - 1; num2 >= 0; num2--) { if (string.Equals(value[num2].RuleId, ruleId, StringComparison.Ordinal)) { value.RemoveAt(num2); result = true; } } if (value.Count == 0) { activeEntriesByPrefab.Remove(prefabName); } } return result; } private static ConfigurationLoadSupport.ParsedLocalConfiguration ParseConfiguration(string yaml, string? sourcePath) { ConfigurationLoadSupport.ParsedLocalConfiguration parsedLocalConfiguration = new ConfigurationLoadSupport.ParsedLocalConfiguration(); if (string.IsNullOrWhiteSpace(yaml)) { return parsedLocalConfiguration; } checked { using StringReader input = new StringReader(yaml); YamlStream yamlStream = new YamlStream(); yamlStream.Load(input); if (yamlStream.Documents.Count == 0) { return parsedLocalConfiguration; } foreach (YamlNode child in ((yamlStream.Documents[0].RootNode as YamlSequenceNode) ?? throw new YamlException(yamlStream.Documents[0].RootNode.Start, yamlStream.Documents[0].RootNode.End, "Object override YAML root must be a sequence.")).Children) { if (!(child is YamlMappingNode yamlMappingNode)) { parsedLocalConfiguration.Warnings.Add("Skipped object YAML node at " + FormatYamlNodeLocation(sourcePath, child.Start) + ". Expected a list item object like '- prefab: wood' but found " + DescribeYamlNode(child) + "."); continue; } try { string input2 = SerializeYamlNode(yamlMappingNode); PrefabConfigurationEntry prefabConfigurationEntry = Deserializer.Deserialize(input2) ?? new PrefabConfigurationEntry(); prefabConfigurationEntry.SourceLine = (int)yamlMappingNode.Start.Line; prefabConfigurationEntry.SourceColumn = (int)yamlMappingNode.Start.Column; parsedLocalConfiguration.Configuration.Add(prefabConfigurationEntry); } catch (Exception ex) { parsedLocalConfiguration.Warnings.Add("Skipped invalid object entry at " + FormatYamlNodeLocation(sourcePath, yamlMappingNode.Start) + ". " + FormatEntryParseFailure(ex)); } } return parsedLocalConfiguration; } } private static string SerializeYamlNode(YamlNode node) { YamlStream yamlStream = new YamlStream(new YamlDocument(node)); using StringWriter stringWriter = new StringWriter(CultureInfo.InvariantCulture); yamlStream.Save(stringWriter, assignAnchors: false); return stringWriter.ToString(); } private static string DescribeYamlNode(YamlNode node) { if (node is YamlScalarNode yamlScalarNode) { string text = yamlScalarNode.Value ?? ""; if (text.Length != 0) { return "scalar '" + text + "'"; } return "an empty scalar"; } if (node is YamlSequenceNode) { return "a nested sequence"; } if (node is YamlMappingNode) { return "a mapping"; } return "an unknown YAML node"; } private static string FormatYamlNodeLocation(string? sourcePath, Mark mark) { string text = (string.IsNullOrWhiteSpace(sourcePath) ? "inline YAML" : Path.GetFileName(sourcePath)); if (mark.Line > 0) { text = text + ":" + mark.Line.ToString(CultureInfo.InvariantCulture); } return text; } private static string FormatEntryParseFailure(Exception ex) { if (ex is YamlException ex2) { return ex2.Message; } return ex.Message; } private static void NormalizeEntry(PrefabConfigurationEntry entry) { entry.Prefab = (entry.Prefab ?? "").Trim(); NormalizeObjectConditions(entry.Conditions, entry.Prefab + ".conditions"); NormalizeDropTable(entry.DropOnDestroyed); NormalizeDropTable(entry.MineRock); NormalizeDropTable(entry.MineRock5); NormalizeDropTable(entry.TreeBase); NormalizeDropTable(entry.TreeLog); NormalizeDropTable(entry.Container); NormalizeDropTablePayload(entry.Pickable?.ExtraDrops); NormalizeDropTablePayload(entry.Fish?.ExtraDrops); NormalizeRandomPickableItems(entry.PickableItem?.RandomDrops); entry.RuleId = NormalizeOptionalRuleId(entry.RuleId) ?? BuildRuleId(entry); } private static void NormalizeObjectConditions(ConditionsDefinition? conditions, string context) { ConditionDialectSupport.StripUnsupportedObjectDropFields(conditions, context, WarnInvalidEntry); } private static string BuildRuleId(PrefabConfigurationEntry entry) { PrefabConfigurationEntry entry2 = new PrefabConfigurationEntry { Prefab = entry.Prefab, Enabled = true, Conditions = entry.Conditions, DropOnDestroyed = entry.DropOnDestroyed, MineRock = entry.MineRock, MineRock5 = entry.MineRock5, TreeBase = entry.TreeBase, TreeLog = entry.TreeLog, Container = entry.Container, Destructible = entry.Destructible, Pickable = entry.Pickable, PickableItem = entry.PickableItem, Fish = entry.Fish }; return entry.Prefab + ":" + NetworkPayloadSyncSupport.ComputeObjectEntryIdentitySignature(entry2); } private static string? NormalizeOptionalRuleId(string? ruleId) { if (ruleId == null) { return null; } string text = ruleId.Trim(); if (text.Length != 0) { return text; } return null; } private static List GetOrCreateActiveEntries(string prefabName) { return GetOrCreateActiveEntries(RuntimeState.ActiveEntriesByPrefab, prefabName); } private static List GetOrCreateActiveEntries(Dictionary> activeEntriesByPrefab, string prefabName) { if (!activeEntriesByPrefab.TryGetValue(prefabName, out List value)) { value = (activeEntriesByPrefab[prefabName] = new List()); } return value; } private static bool CanUseLazyDamageableScalarFastPath(PrefabConfigurationEntry entry, LiveObjectComponentKind componentKind) { if (componentKind != LiveObjectComponentKind.MineRock && componentKind != LiveObjectComponentKind.MineRock5 && componentKind != LiveObjectComponentKind.TreeBase && componentKind != LiveObjectComponentKind.TreeLog) { return false; } return !DropConditionEvaluator.HasDynamicConditions(entry.Conditions); } private static bool HasDropTableOverride(DropTablePayloadDefinition? definition) { if (definition != null) { IntRangeDefinition? rolls = definition.Rolls; if ((rolls == null || !rolls.HasValues()) && !definition.DropMin.HasValue && !definition.DropMax.HasValue && !definition.DropChance.HasValue && !definition.OneOfEach.HasValue) { return definition.Drops != null; } return true; } return false; } private static bool HasDamageableHealthOverride(DamageableDropTableDefinition? definition) { return definition?.Health.HasValue ?? false; } private static bool HasDamageableMinToolTierOverride(DamageableDropTableDefinition? definition) { return definition?.MinToolTier.HasValue ?? false; } private static bool HasDamageableOverride(DamageableDropTableDefinition? definition) { if (!HasDropTableOverride(definition) && !HasDamageableHealthOverride(definition)) { return HasDamageableMinToolTierOverride(definition); } return true; } private static bool HasDestructibleHealthOverride(DestructibleDefinition? definition) { return definition?.Health.HasValue ?? false; } private static bool HasDestructibleMinToolTierOverride(DestructibleDefinition? definition) { return definition?.MinToolTier.HasValue ?? false; } private static bool HasDestructibleTypeOverride(DestructibleDefinition? definition) { return !string.IsNullOrWhiteSpace(definition?.DestructibleType); } private static bool HasDestructibleSpawnWhenDestroyedOverride(DestructibleDefinition? definition) { return !string.IsNullOrWhiteSpace(definition?.SpawnWhenDestroyed); } private static bool HasDestructibleComponentStateOverride(DestructibleDefinition? definition) { if (definition != null) { if (string.IsNullOrWhiteSpace(definition.DestructibleType)) { return !string.IsNullOrWhiteSpace(definition.SpawnWhenDestroyed); } return true; } return false; } private static bool HasDestructibleOverride(DestructibleDefinition? definition) { if (!HasDestructibleHealthOverride(definition) && !HasDestructibleMinToolTierOverride(definition)) { return HasDestructibleComponentStateOverride(definition); } return true; } private static void NormalizeDropTable(DropTableDefinition? definition) { if (definition != null) { NormalizeDropTablePayload(definition); } } private static void NormalizeDropTablePayload(DropTablePayloadDefinition? definition) { if (definition == null) { return; } IntRangeDefinition? rolls = definition.Rolls; if (rolls != null && rolls.HasValues()) { definition.DropMin = RangeFormatting.GetMin(definition.Rolls, definition.DropMin); definition.DropMax = RangeFormatting.GetMax(definition.Rolls, definition.DropMin, definition.DropMax); } if (definition.Drops == null) { return; } foreach (DropEntryDefinition drop in definition.Drops) { IntRangeDefinition? stack = drop.Stack; if (stack != null && stack.HasValues()) { drop.StackMin = RangeFormatting.GetMin(drop.Stack, drop.StackMin); drop.StackMax = RangeFormatting.GetMax(drop.Stack, drop.StackMin, drop.StackMax); } } } private static void NormalizeRandomPickableItems(List? items) { if (items == null) { return; } foreach (RandomPickableItemDefinition item in items) { IntRangeDefinition? stack = item.Stack; if (stack != null && stack.HasValues()) { item.StackMin = RangeFormatting.GetMin(item.Stack, item.StackMin); item.StackMax = RangeFormatting.GetMax(item.Stack, item.StackMin, item.StackMax); } } } private static bool HasPickableOverride(PickableDefinition? definition) { if (definition != null) { if (!HasPickableDropOverride(definition.Drop) && definition.OverrideName == null) { return HasDropTableOverride(definition.ExtraDrops); } return true; } return false; } private static bool HasPickableDropOverride(PickableDropDefinition? definition) { if (definition != null) { if (string.IsNullOrWhiteSpace(definition.Item) && !definition.Amount.HasValue && !definition.MinAmountScaled.HasValue) { return definition.DontScale.HasValue; } return true; } return false; } private static bool HasPickableItemDropOverride(PickableItemDropDefinition? definition) { if (definition != null) { if (string.IsNullOrWhiteSpace(definition.Item)) { return definition.Stack.HasValue; } return true; } return false; } private static bool HasPickableItemOverride(PickableItemDefinition? definition) { if (definition != null) { if (!HasPickableItemDropOverride(definition.Drop)) { return definition.RandomDrops != null; } return true; } return false; } private static bool HasFishOverride(FishDefinition? definition) { if (definition != null) { return HasDropTableOverride(definition.ExtraDrops); } return false; } private static bool IsEventOnlyDropTableFastPathKind(LiveObjectComponentKind componentKind) { if (componentKind != LiveObjectComponentKind.DropOnDestroyed && componentKind != LiveObjectComponentKind.MineRock && componentKind != LiveObjectComponentKind.MineRock5 && componentKind != LiveObjectComponentKind.TreeBase && componentKind != LiveObjectComponentKind.TreeLog) { return componentKind == LiveObjectComponentKind.Container; } return true; } private static bool UsesLiveDropTableReconcile(LiveObjectComponentKind componentKind) { return !IsEventOnlyDropTableFastPathKind(componentKind); } private static bool RequiresLiveReconcile(DropTableDefinition? definition, LiveObjectComponentKind componentKind) { if (definition != null) { return !IsEventOnlyDropTableFastPathKind(componentKind); } return false; } private static bool RequiresLiveReconcile(DamageableDropTableDefinition? definition, LiveObjectComponentKind componentKind) { if (definition != null) { if (!HasDamageableHealthOverride(definition) && !HasDamageableMinToolTierOverride(definition)) { if (!IsEventOnlyDropTableFastPathKind(componentKind)) { return HasDropTableOverride(definition); } return false; } return true; } return false; } private static bool RequiresLiveReconcile(PrefabConfigurationEntry entry, DestructibleDefinition? definition) { if (definition == null) { return false; } if (HasDestructibleHealthOverride(definition) || HasDestructibleMinToolTierOverride(definition) || HasDestructibleTypeOverride(definition)) { return !CanUseLazyDestructibleScalarFastPath(entry); } return false; } private static bool CanUseLazyDestructibleScalarFastPath(PrefabConfigurationEntry entry) { return !DropConditionEvaluator.HasDynamicConditions(entry.Conditions); } private static bool ShouldReconcileLocally(GameObject gameObject) { return PluginSettingsFacade.IsObjectDomainEnabled(); } private static bool TryGetConditionalContext(GameObject gameObject, out string prefabName, out PrefabSnapshot snapshot, out List entries) { snapshot = null; entries = null; if (!CanUseCurrentRuntimeState()) { prefabName = ""; return false; } prefabName = GetPrefabName(gameObject); if (!RuntimeState.ActiveEntriesByPrefab.TryGetValue(prefabName, out entries) || entries.Count == 0 || !SnapshotState.SnapshotsByPrefab.TryGetValue(prefabName, out snapshot)) { return false; } if (!ShouldApplyToInstance(gameObject)) { return false; } return true; } private static bool TryGetConditionalDropContext(GameObject gameObject, out string prefabName, out PrefabSnapshot snapshot, out List compiledRules) { compiledRules = null; if (!TryGetConditionalContext(gameObject, out prefabName, out snapshot, out List _)) { return false; } EnsureRuntimeDropConfigurationState(); if (DropRuntimeState.Configuration.PlansByPrefab.TryGetValue(prefabName, out CompiledObjectPrefabPlan value) && (compiledRules = value.Rules) != null) { return compiledRules.Count > 0; } return false; } private static bool TryGetCompiledObjectDropRule(PrefabConfigurationEntry entry, out CompiledObjectDropRule? compiledRule) { EnsureRuntimeDropConfigurationState(); return DropRuntimeState.Configuration.RulesByEntry.TryGetValue(entry, out compiledRule); } private static bool TryGetStaticDropTableTemplate(string prefabName, LiveObjectComponentKind componentKind, out StaticCompiledDropTableTemplate? template) { template = null; EnsureRuntimeDropConfigurationState(); if (DropRuntimeState.Configuration.PlansByPrefab.TryGetValue(prefabName, out CompiledObjectPrefabPlan value)) { return value.StaticDropTableTemplates.TryGetValue(componentKind, out template); } return false; } internal static bool TryOverrideConditionalDropOnDestroyed(DropOnDestroyed dropOnDestroyed, out DropTable? previous) { lock (Sync) { previous = null; if (!TryGetCachedEventDropTable(((Component)dropOnDestroyed).gameObject, (CompiledObjectDropRule rule) => rule.DropOnDestroyed, (PrefabSnapshot snapshot) => snapshot.DropOnDestroyed, LiveObjectComponentKind.DropOnDestroyed, out DropTable overrideTable)) { return false; } previous = dropOnDestroyed.m_dropWhenDestroyed; dropOnDestroyed.m_dropWhenDestroyed = overrideTable; return true; } } internal static bool TryOverrideConditionalMineRockDrops(MineRock mineRock, out DropTable? previous) { lock (Sync) { previous = null; if (!TryGetCachedEventDropTable(((Component)mineRock).gameObject, (CompiledObjectDropRule rule) => rule.MineRock, (PrefabSnapshot snapshot) => snapshot.MineRock, LiveObjectComponentKind.MineRock, out DropTable overrideTable)) { return false; } previous = mineRock.m_dropItems; mineRock.m_dropItems = overrideTable; return true; } } internal static bool TryOverrideConditionalMineRock5Drops(MineRock5 mineRock5, out DropTable? previous) { lock (Sync) { previous = null; if (!TryGetCachedEventDropTable(((Component)mineRock5).gameObject, (CompiledObjectDropRule rule) => rule.MineRock5, (PrefabSnapshot snapshot) => snapshot.MineRock5, LiveObjectComponentKind.MineRock5, out DropTable overrideTable)) { return false; } previous = mineRock5.m_dropItems; mineRock5.m_dropItems = overrideTable; return true; } } internal static bool TryOverrideContainerDrops(Container container, out DropTable? previous) { lock (Sync) { previous = null; if (!TryGetEffectiveContainerDropTable(((Component)container).gameObject, out DropTable overrideTable)) { return false; } previous = container.m_defaultItems; container.m_defaultItems = overrideTable; return true; } } private static bool TryGetEffectiveContainerDropTable(GameObject gameObject, out DropTable? overrideTable) { return TryGetCachedEventDropTable(gameObject, (CompiledObjectDropRule rule) => rule.Container, (PrefabSnapshot snapshot) => snapshot.Container, LiveObjectComponentKind.Container, out overrideTable); } internal static bool TryOverrideConditionalTreeBaseDrops(TreeBase treeBase, out DropTable? previous) { lock (Sync) { previous = null; if (!TryGetCachedEventDropTable(((Component)treeBase).gameObject, (CompiledObjectDropRule rule) => rule.TreeBase, (PrefabSnapshot snapshot) => snapshot.TreeBase, LiveObjectComponentKind.TreeBase, out DropTable overrideTable)) { return false; } previous = treeBase.m_dropWhenDestroyed; treeBase.m_dropWhenDestroyed = overrideTable; return true; } } internal static bool TryOverrideConditionalTreeLogDrops(TreeLog treeLog, out DropTable? previous) { lock (Sync) { previous = null; if (!TryGetCachedEventDropTable(((Component)treeLog).gameObject, (CompiledObjectDropRule rule) => rule.TreeLog, (PrefabSnapshot snapshot) => snapshot.TreeLog, LiveObjectComponentKind.TreeLog, out DropTable overrideTable)) { return false; } previous = treeLog.m_dropWhenDestroyed; treeLog.m_dropWhenDestroyed = overrideTable; return true; } } internal static bool TryOverrideConditionalDestructibleSpawnWhenDestroyed(Destructible destructible, out GameObject? previous) { lock (Sync) { previous = null; if (!TryGetConditionalDropContext(((Component)destructible).gameObject, out string _, out PrefabSnapshot _, out List compiledRules)) { return false; } GameObject spawnWhenDestroyed = null; bool flag = false; foreach (CompiledObjectDropRule item in compiledRules) { CompiledDestructibleComponentDefinition? destructible2 = item.Destructible; if (destructible2 != null && destructible2.HasSpawnWhenDestroyedOverride && EntryMatches(((Component)destructible).gameObject, item.Entry, allowConditionalMatches: true)) { flag = true; spawnWhenDestroyed = item.Destructible.SpawnWhenDestroyed; } } if (!flag) { return false; } previous = destructible.m_spawnWhenDestroyed; destructible.m_spawnWhenDestroyed = spawnWhenDestroyed; return true; } } private static IEnumerable EnumerateOverrideConfigurationPaths() { return DomainConfigurationFileSupport.EnumerateOverrideConfigurationPaths("object", PrimaryOverrideConfigurationPathYml, PrimaryOverrideConfigurationPathYaml); } private static bool IsOverrideConfigurationFileName(string fileName) { return DomainConfigurationFileSupport.IsOverrideConfigurationFileName("object", fileName); } private static void CaptureSnapshotsIfNeeded() { if (SnapshotState.Snapshots.Count > 0) { return; } foreach (GameObject item in EnumerateRelevantPrefabs()) { PrefabSnapshot prefabSnapshot = CaptureSnapshot(item); if (prefabSnapshot != null) { SnapshotState.Snapshots.Add(prefabSnapshot); if (!SnapshotState.SnapshotsByPrefab.ContainsKey(((Object)prefabSnapshot.Prefab).name)) { SnapshotState.SnapshotsByPrefab.Add(((Object)prefabSnapshot.Prefab).name, prefabSnapshot); } } } } private static IEnumerable EnumeratePrefabs() { HashSet seen = new HashSet(); if ((Object)(object)ZNetScene.instance == (Object)null) { yield break; } foreach (GameObject prefab in ZNetScene.instance.m_prefabs) { if ((Object)(object)prefab != (Object)null && !((Object)prefab).name.StartsWith("JVLmock_", StringComparison.OrdinalIgnoreCase) && seen.Add(((Object)prefab).GetInstanceID())) { yield return prefab; } } foreach (GameObject nonNetViewPrefab in ZNetScene.instance.m_nonNetViewPrefabs) { if ((Object)(object)nonNetViewPrefab != (Object)null && !((Object)nonNetViewPrefab).name.StartsWith("JVLmock_", StringComparison.OrdinalIgnoreCase) && seen.Add(((Object)nonNetViewPrefab).GetInstanceID())) { yield return nonNetViewPrefab; } } } private static IEnumerable EnumerateRelevantPrefabs() { foreach (GameObject item in EnumeratePrefabs()) { if (HasReferenceRelevantComponents(item)) { yield return item; } } } private static int ComputeGameDataSignature() { int frameCount = Time.frameCount; if (_cachedGameDataSignatureFrame == frameCount) { return _cachedGameDataSignatureValue; } int num = ComputeGameDataSignatureCore(); _cachedGameDataSignatureFrame = frameCount; _cachedGameDataSignatureValue = num; return num; } private static int ComputeGameDataSignatureCore() { if (!IsGameDataReady() || (Object)(object)ZNetScene.instance == (Object)null || (Object)(object)ObjectDB.instance == (Object)null) { return 0; } return HashGameObjectCollection(HashGameObjectCollection((17 * 31 + ((Object)ZNetScene.instance).GetInstanceID()) * 31 + ((Object)ObjectDB.instance).GetInstanceID(), EnumerateRelevantPrefabs()), ObjectDB.instance.m_items); } private static int ComputeSnapshotSignature() { int frameCount = Time.frameCount; if (_cachedSnapshotSignatureFrame == frameCount) { return _cachedSnapshotSignatureValue; } int num = ComputeSnapshotSignatureCore(); _cachedSnapshotSignatureFrame = frameCount; _cachedSnapshotSignatureValue = num; return num; } private static int ComputeSnapshotSignatureCore() { if (!IsGameDataReady() || (Object)(object)ZNetScene.instance == (Object)null || (Object)(object)ObjectDB.instance == (Object)null) { return 0; } return HashNamedGameObjectCollection(HashNamedGameObjectCollection(17, EnumerateRelevantPrefabs()), ObjectDB.instance.m_items); } private static string ComputeReferenceSourceSignature() { return ReferenceRefreshSupport.ComputeStableHashForKeys(from prefab in EnumerateRelevantPrefabs() select ((Object)prefab).name); } private static int HashGameObjectCollection(int hash, IEnumerable prefabs) { foreach (GameObject prefab in prefabs) { hash = hash * 31 + (((Object)(object)prefab != (Object)null) ? ((Object)prefab).GetInstanceID() : 0); } return hash; } private static int HashNamedGameObjectCollection(int hash, IEnumerable prefabs) { List list = (from prefab in prefabs where (Object)(object)prefab != (Object)null select ((Object)prefab).name).OrderBy((string name) => name, StringComparer.Ordinal).ToList(); hash = hash * 31 + list.Count; foreach (string item in list) { hash = hash * 31 + item.GetHashCode(); } return hash; } private static IEnumerable BuildSupplementalLocationReferenceEntries(HashSet existingPrefabs) { foreach (KeyValuePair item in BuildLocationReferenceBuckets().OrderBy, string>((KeyValuePair pair) => pair.Key, StringComparer.OrdinalIgnoreCase)) { item.Deconstruct(out var key, out var value); string key2 = key; LocationReferenceBucket locationReferenceBucket = value; string text = ReferenceRefreshSupport.NormalizeKey(key2); if (text.Length != 0 && !existingPrefabs.Contains(text) && locationReferenceBucket.ReferenceEntriesBySignature.Count == 1) { PrefabReferenceEntry prefabReferenceEntry = locationReferenceBucket.ReferenceEntriesBySignature.Values.First(); existingPrefabs.Add(text); yield return prefabReferenceEntry; } } } private static Dictionary BuildLocationReferenceBuckets() { Dictionary dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); foreach (var item2 in EnumerateLocationRootPrefabs()) { string item = item2.LocationPrefab; Transform[] componentsInChildren = item2.RootPrefab.GetComponentsInChildren(true); foreach (Transform val in componentsInChildren) { GameObject val2 = (((Object)(object)val != (Object)null) ? ((Component)val).gameObject : null); if ((Object)(object)val2 == (Object)null) { continue; } PrefabReferenceEntry prefabReferenceEntry = CaptureLocationReferenceEntry(val2); if (prefabReferenceEntry == null) { continue; } if (!dictionary.TryGetValue(prefabReferenceEntry.Prefab, out var value)) { value = new LocationReferenceBucket(); dictionary[prefabReferenceEntry.Prefab] = value; } value.Locations.Add(item); foreach (string locationReferenceComponent in GetLocationReferenceComponents(val2)) { value.Components.Add(locationReferenceComponent); } string key = Serializer.Serialize(prefabReferenceEntry).TrimEnd('\r', '\n'); value.ReferenceEntriesBySignature.TryAdd(key, prefabReferenceEntry); } } return dictionary; } private static IEnumerable<(string LocationPrefab, GameObject RootPrefab)> EnumerateLocationRootPrefabs() { if ((Object)(object)ZoneSystem.instance == (Object)null) { yield break; } HashSet seen = new HashSet(StringComparer.OrdinalIgnoreCase); foreach (ZoneLocation location in ZoneSystem.instance.m_locations) { if (!location.m_prefab.IsValid) { continue; } string text = (location.m_prefab.Name ?? "").Trim(); if (text.Length != 0 && seen.Add(text)) { location.m_prefab.Load(); GameObject asset = location.m_prefab.Asset; if (!((Object)(object)asset == (Object)null)) { yield return (LocationPrefab: text, RootPrefab: asset); } } } } private static PrefabSnapshot? CaptureSnapshot(GameObject prefab) { //IL_0255: Unknown result type (might be due to invalid IL or missing references) DropOnDestroyed component = prefab.GetComponent(); MineRock component2 = prefab.GetComponent(); MineRock5 component3 = prefab.GetComponent(); TreeBase component4 = prefab.GetComponent(); TreeLog component5 = prefab.GetComponent(); Container component6 = prefab.GetComponent(); Pickable component7 = prefab.GetComponent(); PickableItem component8 = prefab.GetComponent(); Fish component9 = prefab.GetComponent(); Destructible component10 = prefab.GetComponent(); if ((Object)(object)component == (Object)null && (Object)(object)component2 == (Object)null && (Object)(object)component3 == (Object)null && (Object)(object)component4 == (Object)null && (Object)(object)component5 == (Object)null && (Object)(object)component6 == (Object)null && (Object)(object)component7 == (Object)null && (Object)(object)component8 == (Object)null && (Object)(object)component9 == (Object)null && (Object)(object)component10 == (Object)null) { return null; } return new PrefabSnapshot { Prefab = prefab, Health = CreateHealthSnapshot(component10, component2, component3, component4, component5), MinToolTier = CreateMinToolTierSnapshot(component10, component2, component3, component4, component5), DropOnDestroyed = (((Object)(object)component != (Object)null) ? CloneDropTable(component.m_dropWhenDestroyed) : null), MineRock = (((Object)(object)component2 != (Object)null) ? CloneDropTable(component2.m_dropItems) : null), MineRock5 = (((Object)(object)component3 != (Object)null) ? CloneDropTable(component3.m_dropItems) : null), TreeBase = (((Object)(object)component4 != (Object)null) ? CloneDropTable(component4.m_dropWhenDestroyed) : null), TreeLog = (((Object)(object)component5 != (Object)null) ? CloneDropTable(component5.m_dropWhenDestroyed) : null), Container = (((Object)(object)component6 != (Object)null) ? CloneDropTable(component6.m_defaultItems) : null), Pickable = (((Object)(object)component7 != (Object)null) ? new PickableSnapshot { ItemPrefab = component7.m_itemPrefab, Amount = component7.m_amount, MinAmountScaled = component7.m_minAmountScaled, DontScale = component7.m_dontScale, OverrideName = component7.m_overrideName, ExtraDrops = CloneDropTable(component7.m_extraDrops) } : null), PickableItem = (((Object)(object)component8 != (Object)null) ? CapturePickableItemSnapshot(component8) : null), Fish = (((Object)(object)component9 != (Object)null) ? new FishSnapshot { ExtraDrops = CloneDropTable(component9.m_extraDrops) } : null), Destructible = (((Object)(object)component10 != (Object)null) ? new DestructibleSnapshot { DestructibleType = component10.m_destructibleType, SpawnWhenDestroyed = component10.m_spawnWhenDestroyed } : null) }; } private static PrefabReferenceEntry? CaptureLocationReferenceEntry(GameObject gameObject) { if ((Object)(object)gameObject == (Object)null) { return null; } PrefabSnapshot prefabSnapshot = CaptureSnapshot(gameObject); if (prefabSnapshot == null) { return null; } string prefabName = GetPrefabName(gameObject); if (string.IsNullOrWhiteSpace(prefabName)) { return null; } prefabSnapshot.Prefab = gameObject; PrefabConfigurationEntry prefabConfigurationEntry = BuildConfigurationEntry(prefabSnapshot); prefabConfigurationEntry.Prefab = prefabName; return new PrefabReferenceEntry { Prefab = prefabConfigurationEntry.Prefab, DropOnDestroyed = prefabConfigurationEntry.DropOnDestroyed, MineRock = prefabConfigurationEntry.MineRock, MineRock5 = prefabConfigurationEntry.MineRock5, TreeBase = prefabConfigurationEntry.TreeBase, TreeLog = prefabConfigurationEntry.TreeLog, Container = prefabConfigurationEntry.Container, Destructible = prefabConfigurationEntry.Destructible, Pickable = prefabConfigurationEntry.Pickable, PickableItem = prefabConfigurationEntry.PickableItem, Fish = prefabConfigurationEntry.Fish }; } private static IEnumerable GetLocationReferenceComponents(GameObject gameObject) { if (!((Object)(object)gameObject == (Object)null)) { if ((Object)(object)gameObject.GetComponent() != (Object)null) { yield return "dropOnDestroyed"; } if ((Object)(object)gameObject.GetComponent() != (Object)null) { yield return "mineRock"; } if ((Object)(object)gameObject.GetComponent() != (Object)null) { yield return "mineRock5"; } if ((Object)(object)gameObject.GetComponent() != (Object)null) { yield return "treeBase"; } if ((Object)(object)gameObject.GetComponent() != (Object)null) { yield return "treeLog"; } if ((Object)(object)gameObject.GetComponent() != (Object)null) { yield return "container"; } if ((Object)(object)gameObject.GetComponent() != (Object)null) { yield return "pickable"; } if ((Object)(object)gameObject.GetComponent() != (Object)null) { yield return "pickableItem"; } if ((Object)(object)gameObject.GetComponent() != (Object)null) { yield return "fish"; } if ((Object)(object)gameObject.GetComponent() != (Object)null) { yield return "destructible"; } } } private static bool HasReferenceRelevantComponents(GameObject prefab) { return HasRelevantLiveObjectComponents(prefab); } private static HealthSnapshot? CreateHealthSnapshot(Destructible? destructible, MineRock? mineRock, MineRock5? mineRock5, TreeBase? treeBase, TreeLog? treeLog) { if ((Object)(object)destructible == (Object)null && (Object)(object)mineRock == (Object)null && (Object)(object)mineRock5 == (Object)null && (Object)(object)treeBase == (Object)null && (Object)(object)treeLog == (Object)null) { return null; } return new HealthSnapshot { Destructible = destructible?.m_health, MineRock = mineRock?.m_health, MineRock5 = mineRock5?.m_health, TreeBase = treeBase?.m_health, TreeLog = treeLog?.m_health }; } private static MinToolTierSnapshot? CreateMinToolTierSnapshot(Destructible? destructible, MineRock? mineRock, MineRock5? mineRock5, TreeBase? treeBase, TreeLog? treeLog) { if ((Object)(object)destructible == (Object)null && (Object)(object)mineRock == (Object)null && (Object)(object)mineRock5 == (Object)null && (Object)(object)treeBase == (Object)null && (Object)(object)treeLog == (Object)null) { return null; } return new MinToolTierSnapshot { Destructible = destructible?.m_minToolTier, MineRock = mineRock?.m_minToolTier, MineRock5 = mineRock5?.m_minToolTier, TreeBase = treeBase?.m_minToolTier, TreeLog = treeLog?.m_minToolTier }; } private static PickableItemSnapshot CapturePickableItemSnapshot(PickableItem pickableItem) { //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_0052: 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_0073: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) PickableItemSnapshot pickableItemSnapshot = new PickableItemSnapshot { ItemPrefab = (Object.op_Implicit((Object)(object)pickableItem.m_itemPrefab) ? ((Component)pickableItem.m_itemPrefab).gameObject : null), Stack = pickableItem.m_stack }; RandomItem[] randomItemPrefabs = pickableItem.m_randomItemPrefabs; foreach (RandomItem val in randomItemPrefabs) { pickableItemSnapshot.RandomItems.Add(new PickableItemRandomSnapshot { ItemPrefab = (Object.op_Implicit((Object)(object)val.m_itemPrefab) ? ((Component)val.m_itemPrefab).gameObject : null), StackMin = val.m_stackMin, StackMax = val.m_stackMax }); } return pickableItemSnapshot; } private static void ApplyIfReady(bool queueLiveReconcile = false) { if (!IsGameDataReady() || SnapshotState.Snapshots.Count == 0) { return; } bool flag = Volatile.Read(in _synchronizedPayloadReady); if (StandardDomainApplySupport.CanApplySynchronizedDomain(flag)) { int num = ComputeGameDataSignature(); bool flag2 = PluginSettingsFacade.IsObjectDomainEnabled(); Dictionary currentEntrySignatures = BuildActiveEntrySignaturesByPrefab(); if (!StandardDomainApplySupport.IsAlreadyApplied(_lastAppliedGameDataSignature, num, _lastAppliedDomainEnabled, flag2, _lastAppliedConfigurationSignature, RuntimeState.ConfigurationSignature, _lastAppliedSynchronizedPayloadReady, flag)) { RunApplyCoordinator(num, flag2, currentEntrySignatures, queueLiveReconcile); } } } private static void EnsureRuntimeDropConfigurationState() { if (IsGameDataReady()) { int num = ComputeGameDataSignature(); if (DropRuntimeState.GameDataSignature != num || !string.Equals(DropRuntimeState.ConfigurationSignature, RuntimeState.ConfigurationSignature, StringComparison.Ordinal)) { DropRuntimeState.Configuration = BuildRuntimeDropConfigurationState(); DropRuntimeState.GameDataSignature = num; DropRuntimeState.ConfigurationSignature = RuntimeState.ConfigurationSignature; } } } private static ObjectRuntimeDropConfigurationState BuildRuntimeDropConfigurationState() { ObjectRuntimeDropConfigurationState objectRuntimeDropConfigurationState = new ObjectRuntimeDropConfigurationState(); foreach (KeyValuePair> item in RuntimeState.ActiveEntriesByPrefab) { item.Deconstruct(out var key, out var value); string text = key; List list = value; CompiledObjectPrefabPlan compiledObjectPrefabPlan = new CompiledObjectPrefabPlan(); compiledObjectPrefabPlan.ActiveEntries.AddRange(list); foreach (PrefabConfigurationEntry item2 in list) { CompiledObjectDropRule compiledObjectDropRule = CompileObjectDropRule(item2); if (compiledObjectDropRule != null) { compiledObjectPrefabPlan.Rules.Add(compiledObjectDropRule); objectRuntimeDropConfigurationState.RulesByEntry[item2] = compiledObjectDropRule; } } if (compiledObjectPrefabPlan.ActiveEntries.Count > 0) { BuildStaticDropTableTemplatesForPrefab(text, compiledObjectPrefabPlan); objectRuntimeDropConfigurationState.PlansByPrefab[text] = compiledObjectPrefabPlan; } } return objectRuntimeDropConfigurationState; } private static void BuildStaticDropTableTemplatesForPrefab(string prefabName, CompiledObjectPrefabPlan plan) { if (!SnapshotState.SnapshotsByPrefab.TryGetValue(prefabName, out PrefabSnapshot value)) { return; } Dictionary dictionary = new Dictionary(); TryAddStaticDropTableTemplate(dictionary, plan.Rules, value.DropOnDestroyed, LiveObjectComponentKind.DropOnDestroyed, (CompiledObjectDropRule rule) => rule.DropOnDestroyed); TryAddStaticDropTableTemplate(dictionary, plan.Rules, value.MineRock, LiveObjectComponentKind.MineRock, (CompiledObjectDropRule rule) => rule.MineRock); TryAddStaticDropTableTemplate(dictionary, plan.Rules, value.MineRock5, LiveObjectComponentKind.MineRock5, (CompiledObjectDropRule rule) => rule.MineRock5); TryAddStaticDropTableTemplate(dictionary, plan.Rules, value.TreeBase, LiveObjectComponentKind.TreeBase, (CompiledObjectDropRule rule) => rule.TreeBase); TryAddStaticDropTableTemplate(dictionary, plan.Rules, value.TreeLog, LiveObjectComponentKind.TreeLog, (CompiledObjectDropRule rule) => rule.TreeLog); TryAddStaticDropTableTemplate(dictionary, plan.Rules, value.Container, LiveObjectComponentKind.Container, (CompiledObjectDropRule rule) => rule.Container); TryAddStaticDropTableTemplate(dictionary, plan.Rules, value.Pickable?.ExtraDrops, LiveObjectComponentKind.Pickable, (CompiledObjectDropRule rule) => rule.PickableExtraDrops); TryAddStaticDropTableTemplate(dictionary, plan.Rules, value.Fish?.ExtraDrops, LiveObjectComponentKind.Fish, (CompiledObjectDropRule rule) => rule.FishExtraDrops); if (dictionary.Count <= 0) { return; } foreach (var (key, value2) in dictionary) { plan.StaticDropTableTemplates[key] = value2; } } private static void TryAddStaticDropTableTemplate(Dictionary templates, IEnumerable compiledRules, DropTable? snapshotTable, LiveObjectComponentKind componentKind, Func payloadSelector) { List list = new List(); foreach (CompiledObjectDropRule item in compiledRules ?? Enumerable.Empty()) { CompiledDropTablePayload compiledDropTablePayload = payloadSelector(item); if (compiledDropTablePayload != null && !item.HasConditions) { list.Add(compiledDropTablePayload); } } if (list.Count != 0) { templates[componentKind] = BuildStaticDropTableTemplate(snapshotTable, list); } } private static StaticCompiledDropTableTemplate BuildStaticDropTableTemplate(DropTable? snapshotTable, IEnumerable payloads) { StaticCompiledDropTableTemplate staticCompiledDropTableTemplate = new StaticCompiledDropTableTemplate { Template = ((snapshotTable != null) ? CloneDropTable(snapshotTable) : CreateDefaultDropTable()) }; staticCompiledDropTableTemplate.Template.m_drops = new List(); foreach (CompiledDropTablePayload item in payloads ?? Enumerable.Empty()) { ApplyDropTableScalarOverrides(staticCompiledDropTableTemplate.Template, item); AppendDropTableRows(staticCompiledDropTableTemplate.Template.m_drops, item.Drops, staticCompiledDropTableTemplate.Fingerprints); } return staticCompiledDropTableTemplate; } private static CompiledObjectDropRule? CompileObjectDropRule(PrefabConfigurationEntry entry) { CompiledObjectDropRule compiledObjectDropRule = new CompiledObjectDropRule { Entry = entry, HasConditions = DropConditionEvaluator.HasConditions(entry.Conditions), DropOnDestroyed = CompileDropTablePayload(entry, entry.DropOnDestroyed, "DropOnDestroyed"), MineRock = CompileDropTablePayload(entry, entry.MineRock, "MineRock"), MineRock5 = CompileDropTablePayload(entry, entry.MineRock5, "MineRock5"), TreeBase = CompileDropTablePayload(entry, entry.TreeBase, "TreeBase"), TreeLog = CompileDropTablePayload(entry, entry.TreeLog, "TreeLog"), Container = CompileDropTablePayload(entry, entry.Container, "Container"), PickableExtraDrops = CompileDropTablePayload(entry, entry.Pickable?.ExtraDrops, "Pickable/ExtraDrops"), FishExtraDrops = CompileDropTablePayload(entry, entry.Fish?.ExtraDrops, "Fish/ExtraDrops"), Pickable = CompilePickableDefinition(entry), PickableItem = CompilePickableItemDefinition(entry), Destructible = CompileDestructibleComponentDefinition(entry), MineRockScalars = CompileDamageableScalarDefinition(entry, entry.MineRock, "MineRock"), MineRock5Scalars = CompileDamageableScalarDefinition(entry, entry.MineRock5, "MineRock5"), TreeBaseScalars = CompileDamageableScalarDefinition(entry, entry.TreeBase, "TreeBase"), TreeLogScalars = CompileDamageableScalarDefinition(entry, entry.TreeLog, "TreeLog") }; if (compiledObjectDropRule.DropOnDestroyed == null && compiledObjectDropRule.MineRock == null && compiledObjectDropRule.MineRock5 == null && compiledObjectDropRule.TreeBase == null && compiledObjectDropRule.TreeLog == null && compiledObjectDropRule.Container == null && compiledObjectDropRule.PickableExtraDrops == null && compiledObjectDropRule.FishExtraDrops == null && compiledObjectDropRule.Pickable == null && compiledObjectDropRule.PickableItem == null && compiledObjectDropRule.Destructible == null && compiledObjectDropRule.MineRockScalars == null && compiledObjectDropRule.MineRock5Scalars == null && compiledObjectDropRule.TreeBaseScalars == null && compiledObjectDropRule.TreeLogScalars == null) { return null; } return compiledObjectDropRule; } private static CompiledDamageableScalarDefinition? CompileDamageableScalarDefinition(PrefabConfigurationEntry entry, DamageableDropTableDefinition? definition, string componentName) { if (!HasDamageableOverride(definition)) { return null; } string text = BuildCompiledObjectDropContext(entry, componentName); CompiledDamageableScalarDefinition compiledDamageableScalarDefinition = new CompiledDamageableScalarDefinition(); if (HasDamageableHealthOverride(definition) && TryGetConfiguredHealth(definition.Health.Value, text + "/Health", out var normalizedHealth)) { compiledDamageableScalarDefinition.HasHealthOverride = true; compiledDamageableScalarDefinition.Health = normalizedHealth; } if (HasDamageableMinToolTierOverride(definition) && TryGetConfiguredMinToolTier(definition.MinToolTier.Value, text + "/minToolTier", out var normalizedMinToolTier)) { compiledDamageableScalarDefinition.HasMinToolTierOverride = true; compiledDamageableScalarDefinition.MinToolTier = normalizedMinToolTier; } if (!compiledDamageableScalarDefinition.HasHealthOverride && !compiledDamageableScalarDefinition.HasMinToolTierOverride) { return null; } return compiledDamageableScalarDefinition; } private static CompiledDestructibleComponentDefinition? CompileDestructibleComponentDefinition(PrefabConfigurationEntry entry) { //IL_00c0: Unknown result type (might be due to invalid IL or missing references) if (!HasDestructibleOverride(entry.Destructible)) { return null; } DestructibleDefinition destructible = entry.Destructible; string text = BuildCompiledObjectDropContext(entry, "Destructible"); CompiledDestructibleComponentDefinition compiledDestructibleComponentDefinition = new CompiledDestructibleComponentDefinition(); if (HasDestructibleHealthOverride(destructible) && TryGetConfiguredHealth(destructible.Health.Value, text + "/Health", out var normalizedHealth)) { compiledDestructibleComponentDefinition.HasHealthOverride = true; compiledDestructibleComponentDefinition.Health = normalizedHealth; } if (HasDestructibleMinToolTierOverride(destructible) && TryGetConfiguredMinToolTier(destructible.MinToolTier.Value, text + "/minToolTier", out var normalizedMinToolTier)) { compiledDestructibleComponentDefinition.HasMinToolTierOverride = true; compiledDestructibleComponentDefinition.MinToolTier = normalizedMinToolTier; } if (!string.IsNullOrWhiteSpace(destructible.DestructibleType)) { if (Enum.TryParse(destructible.DestructibleType, ignoreCase: true, out DestructibleType result)) { compiledDestructibleComponentDefinition.HasDestructibleTypeOverride = true; compiledDestructibleComponentDefinition.DestructibleType = result; } else { WarnInvalidEntry("Entry '" + text + "' has invalid destructibleType '" + destructible.DestructibleType + "'."); } } if (!string.IsNullOrWhiteSpace(destructible.SpawnWhenDestroyed)) { GameObject val = ResolveSpawnPrefab(destructible.SpawnWhenDestroyed, text); if ((Object)(object)val != (Object)null) { compiledDestructibleComponentDefinition.HasSpawnWhenDestroyedOverride = true; compiledDestructibleComponentDefinition.SpawnWhenDestroyed = val; } } if (!compiledDestructibleComponentDefinition.HasHealthOverride && !compiledDestructibleComponentDefinition.HasMinToolTierOverride && !compiledDestructibleComponentDefinition.HasDestructibleTypeOverride && !compiledDestructibleComponentDefinition.HasSpawnWhenDestroyedOverride) { return null; } return compiledDestructibleComponentDefinition; } private static CompiledPickableDefinition? CompilePickableDefinition(PrefabConfigurationEntry entry) { if (!HasPickableOverride(entry.Pickable)) { return null; } PickableDefinition pickable = entry.Pickable; CompiledPickableDefinition compiledPickableDefinition = new CompiledPickableDefinition(); if (HasPickableDropOverride(pickable.Drop)) { if (!string.IsNullOrWhiteSpace(pickable.Drop.Item)) { GameObject val = ResolveItemPrefab(pickable.Drop.Item, BuildCompiledObjectDropContext(entry, "Pickable")); if ((Object)(object)val != (Object)null) { compiledPickableDefinition.HasItemPrefabOverride = true; compiledPickableDefinition.ItemPrefab = val; } } if (pickable.Drop.Amount.HasValue) { compiledPickableDefinition.HasAmountOverride = true; compiledPickableDefinition.Amount = Math.Max(1, pickable.Drop.Amount.Value); } if (pickable.Drop.MinAmountScaled.HasValue) { compiledPickableDefinition.HasMinAmountScaledOverride = true; compiledPickableDefinition.MinAmountScaled = Math.Max(1, pickable.Drop.MinAmountScaled.Value); } if (pickable.Drop.DontScale.HasValue) { compiledPickableDefinition.HasDontScaleOverride = true; compiledPickableDefinition.DontScale = pickable.Drop.DontScale.Value; } } if (pickable.OverrideName != null) { compiledPickableDefinition.HasOverrideNameOverride = true; compiledPickableDefinition.OverrideName = pickable.OverrideName; } return compiledPickableDefinition; } private static CompiledPickableItemDefinition? CompilePickableItemDefinition(PrefabConfigurationEntry entry) { if (!HasPickableItemOverride(entry.PickableItem)) { return null; } PickableItemDefinition pickableItem = entry.PickableItem; CompiledPickableItemDefinition compiledPickableItemDefinition = new CompiledPickableItemDefinition(); bool hasRandomOverride = pickableItem.RandomDrops != null; List list = pickableItem.RandomDrops ?? new List(); bool num = list.Count > 0; bool flag = HasPickableItemDropOverride(pickableItem.Drop); string text = BuildCompiledObjectDropContext(entry, "PickableItem"); if (num && flag) { WarnInvalidEntry("Entry '" + text + "' defines both drop and randomDrops. randomDrops take precedence."); } compiledPickableItemDefinition.HasRandomOverride = hasRandomOverride; if (num) { BuiltRandomPickableItems builtRandomPickableItems = BuildRandomItems(list, text); compiledPickableItemDefinition.RandomItems = builtRandomPickableItems.Items; compiledPickableItemDefinition.RandomWeights = builtRandomPickableItems.Weights; } compiledPickableItemDefinition.HasFixedDrop = flag; if (flag) { if (!string.IsNullOrWhiteSpace(pickableItem.Drop?.Item)) { GameObject val = ResolveItemPrefab(pickableItem.Drop.Item, text); if ((Object)(object)val != (Object)null) { compiledPickableItemDefinition.HasFixedItemOverride = true; compiledPickableItemDefinition.FixedItemPrefab = val.GetComponent(); } } PickableItemDropDefinition? drop = pickableItem.Drop; if (drop != null && drop.Stack.HasValue) { compiledPickableItemDefinition.HasFixedStackOverride = true; compiledPickableItemDefinition.FixedStack = Math.Max(1, pickableItem.Drop.Stack.Value); } } return compiledPickableItemDefinition; } private static CompiledDropTablePayload? CompileDropTablePayload(PrefabConfigurationEntry entry, DropTablePayloadDefinition? definition, string componentName) { if (!HasDropTableOverride(definition)) { return null; } CompiledDropTablePayload compiledDropTablePayload = new CompiledDropTablePayload(); if (definition.DropMin.HasValue || definition.DropMax.HasValue) { compiledDropTablePayload.HasDropRangeOverride = true; compiledDropTablePayload.DropMin = Math.Max(0, definition.DropMin ?? 1); compiledDropTablePayload.DropMax = Math.Max(compiledDropTablePayload.DropMin, definition.DropMax ?? definition.DropMin ?? 1); } if (definition.DropChance.HasValue) { compiledDropTablePayload.HasDropChanceOverride = true; compiledDropTablePayload.DropChance = Mathf.Clamp01(definition.DropChance.Value); } if (definition.OneOfEach.HasValue) { compiledDropTablePayload.HasOneOfEachOverride = true; compiledDropTablePayload.OneOfEach = definition.OneOfEach.Value; } string context = BuildCompiledObjectDropContext(entry, componentName); IEnumerable drops = definition.Drops; foreach (DropEntryDefinition item in drops ?? Enumerable.Empty()) { if (TryCompileDropTableRow(item, context, out CompiledDropTableRow compiledRow)) { compiledDropTablePayload.Drops.Add(compiledRow); } } return compiledDropTablePayload; } private static bool TryCompileDropTableRow(DropEntryDefinition drop, string context, out CompiledDropTableRow compiledRow) { compiledRow = null; string text = (drop.Item ?? "").Trim(); if (text.Length == 0) { WarnInvalidEntry("Entry '" + context + "' contains a drop without an item name."); return false; } GameObject val = ResolveItemPrefab(text, context); if ((Object)(object)val == (Object)null) { return false; } compiledRow = new CompiledDropTableRow { Fingerprint = BuildDropRowFingerprint(drop), ItemPrefab = val, StackMin = Math.Max(1, drop.StackMin ?? 1), StackMax = Math.Max(Math.Max(1, drop.StackMin ?? 1), drop.StackMax ?? drop.StackMin ?? 1), Weight = Mathf.Max(0f, drop.Weight ?? 1f), DontScale = (drop.DontScale == true) }; return true; } private static void ValidateConfiguredPrefabs() { foreach (var (text2, list2) in RuntimeState.ActiveEntriesByPrefab) { if (SnapshotState.SnapshotsByPrefab.ContainsKey(text2)) { continue; } foreach (PrefabConfigurationEntry item in list2) { WarnInvalidEntry("Object prefab '" + text2 + "' from " + DescribeEntrySource(item) + " was not found in ZNetScene."); } } } private static void RecordAppliedState(int gameDataSignature, bool domainEnabled, Dictionary currentEntrySignatures) { _lastAppliedGameDataSignature = gameDataSignature; _lastAppliedDomainEnabled = domainEnabled; _lastAppliedConfigurationSignature = RuntimeState.ConfigurationSignature; _lastAppliedSynchronizedPayloadReady = Volatile.Read(in _synchronizedPayloadReady); ReplaceEntrySignatures(_lastAppliedEntrySignaturesByPrefab, currentEntrySignatures); if (domainEnabled) { PrefabProfileCatalogState.RecordCurrentConfiguredKindsAsLastApplied(); PrefabProfileCatalogState.RecordCurrentReconcileKindsAsLastApplied(); } else { PrefabProfileCatalogState.ClearLastAppliedConfiguredKinds(); PrefabProfileCatalogState.ClearLastAppliedReconcileKinds(); } } private static void RestoreSnapshots(HashSet? targetPrefabs = null) { if (targetPrefabs == null) { foreach (PrefabSnapshot snapshot in SnapshotState.Snapshots) { RestoreConfiguredComponents(snapshot.Prefab, snapshot, CreateRestoreMask(snapshot), updateRuntimeState: false); } return; } foreach (string targetPrefab in targetPrefabs) { if (SnapshotState.SnapshotsByPrefab.TryGetValue(targetPrefab, out PrefabSnapshot value)) { RestoreConfiguredComponents(value.Prefab, value, CreateRestoreMask(value), updateRuntimeState: false); } } } private static void RestoreTrackedLiveObjects(HashSet prefabs) { if (prefabs.Count == 0 || !IsGameDataReady()) { return; } foreach (GameObject registeredLiveObject in GetRegisteredLiveObjects(prefabs)) { if (!((Object)(object)registeredLiveObject == (Object)null)) { string prefabName = GetPrefabName(registeredLiveObject); if (SnapshotState.SnapshotsByPrefab.TryGetValue(prefabName, out PrefabSnapshot value)) { RestoreConfiguredComponents(registeredLiveObject, value, CreateRestoreMask(value), updateRuntimeState: true); } } } } private static PrefabConfigurationEntry CreateRestoreMask(PrefabSnapshot snapshot) { return new PrefabConfigurationEntry { Prefab = ((Object)snapshot.Prefab).name, Destructible = CreateRestoreDestructibleMask(snapshot), DropOnDestroyed = ((snapshot.DropOnDestroyed != null) ? CreateRestoreDropTableMask() : null), MineRock = CreateRestoreDamageableDropTableMask(snapshot.MineRock, snapshot.Health?.MineRock, snapshot.MinToolTier?.MineRock), MineRock5 = CreateRestoreDamageableDropTableMask(snapshot.MineRock5, snapshot.Health?.MineRock5, snapshot.MinToolTier?.MineRock5), TreeBase = CreateRestoreDamageableDropTableMask(snapshot.TreeBase, snapshot.Health?.TreeBase, snapshot.MinToolTier?.TreeBase), TreeLog = CreateRestoreDamageableDropTableMask(snapshot.TreeLog, snapshot.Health?.TreeLog, snapshot.MinToolTier?.TreeLog), Container = ((snapshot.Container != null) ? CreateRestoreDropTableMask() : null), Pickable = ((snapshot.Pickable != null) ? new PickableDefinition { OverrideName = "" } : null), PickableItem = ((snapshot.PickableItem != null) ? new PickableItemDefinition { RandomDrops = new List() } : null), Fish = ((snapshot.Fish != null) ? new FishDefinition { ExtraDrops = CreateRestoreDropTableMask() } : null) }; } private static DropTableDefinition CreateRestoreDropTableMask() { return new DropTableDefinition { Drops = new List() }; } private static DamageableDropTableDefinition? CreateRestoreDamageableDropTableMask(DropTable? dropTable, float? health, int? minToolTier) { if (dropTable == null && !health.HasValue && !minToolTier.HasValue) { return null; } return new DamageableDropTableDefinition { Drops = ((dropTable != null) ? new List() : null), Health = (health.HasValue ? new float?(0f) : ((float?)null)), MinToolTier = (minToolTier.HasValue ? new int?(0) : ((int?)null)) }; } private static DestructibleDefinition? CreateRestoreDestructibleMask(PrefabSnapshot snapshot) { //IL_0097: 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) bool flag = snapshot.Health?.Destructible.HasValue ?? false; bool flag2 = snapshot.MinToolTier?.Destructible.HasValue ?? false; bool flag3 = snapshot.Destructible != null; if (!flag && !flag2 && !flag3) { return null; } return new DestructibleDefinition { Health = (flag ? new float?(0f) : ((float?)null)), MinToolTier = (flag2 ? new int?(0) : ((int?)null)), DestructibleType = (flag3 ? ((object)snapshot.Destructible.DestructibleType/*cast due to .constrained prefix*/).ToString() : null), SpawnWhenDestroyed = (flag3 ? "restore" : null) }; } private static Dictionary BuildActiveEntrySignaturesByPrefab() { return DomainEntrySignatureSupport.BuildSignaturesByKey(RuntimeState.ActiveEntriesByPrefab, NetworkPayloadSyncSupport.ComputeObjectConfigurationSignature); } private static HashSet BuildDirtyPrefabs(Dictionary previous, Dictionary current) { return DomainDictionaryDiffSupport.BuildDirtyKeys(previous, current); } private static void ReplaceEntrySignatures(Dictionary target, Dictionary source) { DomainDictionaryDiffSupport.ReplaceEntries(target, source); } private static void ApplyEffectiveDropTableOverrides(GameObject gameObject, PrefabSnapshot snapshot, IEnumerable entries, bool allowConditionalMatches, GroupConditionalApplyPlan? groupPlan = null, bool includeEventOnlyKinds = true) { EnsureRuntimeDropConfigurationState(); List rules; if (DropRuntimeState.Configuration.PlansByPrefab.TryGetValue(((Object)snapshot.Prefab).name, out CompiledObjectPrefabPlan value) && (rules = value.Rules) != null && rules.Count != 0) { DropOnDestroyed val = default(DropOnDestroyed); if ((includeEventOnlyKinds || (UsesLiveDropTableReconcile(LiveObjectComponentKind.DropOnDestroyed) && RequiresLiveReconcileForPrefab(((Object)snapshot.Prefab).name, LiveObjectComponentKind.DropOnDestroyed))) && gameObject.TryGetComponent(ref val) && TryBuildEffectiveDropTable(gameObject, ((Object)snapshot.Prefab).name, rules, (CompiledObjectDropRule rule) => rule.DropOnDestroyed, snapshot.DropOnDestroyed, LiveObjectComponentKind.DropOnDestroyed, allowConditionalMatches, groupPlan, out DropTable effectiveTable)) { val.m_dropWhenDestroyed = effectiveTable; } MineRock val2 = default(MineRock); if ((includeEventOnlyKinds || (UsesLiveDropTableReconcile(LiveObjectComponentKind.MineRock) && RequiresLiveReconcileForPrefab(((Object)snapshot.Prefab).name, LiveObjectComponentKind.MineRock))) && gameObject.TryGetComponent(ref val2) && TryBuildEffectiveDropTable(gameObject, ((Object)snapshot.Prefab).name, rules, (CompiledObjectDropRule rule) => rule.MineRock, snapshot.MineRock, LiveObjectComponentKind.MineRock, allowConditionalMatches, groupPlan, out DropTable effectiveTable2)) { val2.m_dropItems = effectiveTable2; } MineRock5 val3 = default(MineRock5); if ((includeEventOnlyKinds || (UsesLiveDropTableReconcile(LiveObjectComponentKind.MineRock5) && RequiresLiveReconcileForPrefab(((Object)snapshot.Prefab).name, LiveObjectComponentKind.MineRock5))) && gameObject.TryGetComponent(ref val3) && TryBuildEffectiveDropTable(gameObject, ((Object)snapshot.Prefab).name, rules, (CompiledObjectDropRule rule) => rule.MineRock5, snapshot.MineRock5, LiveObjectComponentKind.MineRock5, allowConditionalMatches, groupPlan, out DropTable effectiveTable3)) { val3.m_dropItems = effectiveTable3; } Container val4 = default(Container); if ((includeEventOnlyKinds || (UsesLiveDropTableReconcile(LiveObjectComponentKind.Container) && RequiresLiveReconcileForPrefab(((Object)snapshot.Prefab).name, LiveObjectComponentKind.Container))) && gameObject.TryGetComponent(ref val4) && TryBuildEffectiveDropTable(gameObject, ((Object)snapshot.Prefab).name, rules, (CompiledObjectDropRule rule) => rule.Container, snapshot.Container, LiveObjectComponentKind.Container, allowConditionalMatches, groupPlan, out DropTable effectiveTable4)) { val4.m_defaultItems = effectiveTable4; } TreeBase val5 = default(TreeBase); if ((includeEventOnlyKinds || (UsesLiveDropTableReconcile(LiveObjectComponentKind.TreeBase) && RequiresLiveReconcileForPrefab(((Object)snapshot.Prefab).name, LiveObjectComponentKind.TreeBase))) && gameObject.TryGetComponent(ref val5) && TryBuildEffectiveDropTable(gameObject, ((Object)snapshot.Prefab).name, rules, (CompiledObjectDropRule rule) => rule.TreeBase, snapshot.TreeBase, LiveObjectComponentKind.TreeBase, allowConditionalMatches, groupPlan, out DropTable effectiveTable5)) { val5.m_dropWhenDestroyed = effectiveTable5; } TreeLog val6 = default(TreeLog); if ((includeEventOnlyKinds || (UsesLiveDropTableReconcile(LiveObjectComponentKind.TreeLog) && RequiresLiveReconcileForPrefab(((Object)snapshot.Prefab).name, LiveObjectComponentKind.TreeLog))) && gameObject.TryGetComponent(ref val6) && TryBuildEffectiveDropTable(gameObject, ((Object)snapshot.Prefab).name, rules, (CompiledObjectDropRule rule) => rule.TreeLog, snapshot.TreeLog, LiveObjectComponentKind.TreeLog, allowConditionalMatches, groupPlan, out DropTable effectiveTable6)) { val6.m_dropWhenDestroyed = effectiveTable6; } Pickable val7 = default(Pickable); if ((includeEventOnlyKinds || RequiresLiveReconcileForPrefab(((Object)snapshot.Prefab).name, LiveObjectComponentKind.Pickable)) && gameObject.TryGetComponent(ref val7) && TryBuildEffectiveDropTable(gameObject, ((Object)snapshot.Prefab).name, rules, (CompiledObjectDropRule rule) => rule.PickableExtraDrops, snapshot.Pickable?.ExtraDrops, LiveObjectComponentKind.Pickable, allowConditionalMatches, groupPlan, out DropTable effectiveTable7)) { val7.m_extraDrops = effectiveTable7; } Fish val8 = default(Fish); if ((includeEventOnlyKinds || RequiresLiveReconcileForPrefab(((Object)snapshot.Prefab).name, LiveObjectComponentKind.Fish)) && gameObject.TryGetComponent(ref val8) && TryBuildEffectiveDropTable(gameObject, ((Object)snapshot.Prefab).name, rules, (CompiledObjectDropRule rule) => rule.FishExtraDrops, snapshot.Fish?.ExtraDrops, LiveObjectComponentKind.Fish, allowConditionalMatches, groupPlan, out DropTable effectiveTable8)) { val8.m_extraDrops = effectiveTable8; } } } private static bool TryBuildEffectiveDropTable(GameObject gameObject, string prefabName, IEnumerable rules, Func payloadSelector, DropTable? snapshotTable, LiveObjectComponentKind componentKind, bool allowConditionalMatches, GroupConditionalApplyPlan? groupPlan, out DropTable? effectiveTable) { effectiveTable = null; StaticCompiledDropTableTemplate template = null; if (prefabName.Length > 0) { TryGetStaticDropTableTemplate(prefabName, componentKind, out template); } List list = new List(); bool flag = false; if (groupPlan != null) { foreach (CompiledObjectDropRule matchingCompiledRule in groupPlan.MatchingCompiledRules) { CompiledDropTablePayload compiledDropTablePayload = payloadSelector(matchingCompiledRule); if (compiledDropTablePayload != null) { flag = true; if (template == null || matchingCompiledRule.HasConditions) { list.Add(compiledDropTablePayload); } } } } foreach (CompiledObjectDropRule item in rules ?? Enumerable.Empty()) { CompiledDropTablePayload compiledDropTablePayload2 = payloadSelector(item); if (compiledDropTablePayload2 != null) { flag = true; if ((groupPlan == null || !groupPlan.EligibleCompiledRules.Contains(item)) && (template == null || item.HasConditions) && EntryMatches(gameObject, item.Entry, allowConditionalMatches)) { list.Add(compiledDropTablePayload2); } } } if (!flag) { return false; } if (template != null) { effectiveTable = BuildEffectiveDropTable(template, list); return true; } effectiveTable = BuildEffectiveDropTable(snapshotTable, list); return true; } private static bool EntryMatches(GameObject gameObject, PrefabConfigurationEntry entry, bool allowConditionalMatches) { if (!DropConditionEvaluator.HasConditions(entry.Conditions)) { return true; } if (allowConditionalMatches) { return DropConditionEvaluator.AreSatisfied(gameObject, entry.Conditions); } return false; } private static bool ShouldApplyToInstance(GameObject gameObject) { Piece component = gameObject.GetComponent(); if ((Object)(object)component == (Object)null) { return true; } return component.GetCreator() == 0; } private static void RestoreConfiguredComponents(GameObject gameObject, PrefabSnapshot snapshot, PrefabConfigurationEntry entry, bool updateRuntimeState) { RestoreResourceComponents(gameObject, snapshot, entry, updateRuntimeState); RestoreInteractiveComponents(gameObject, snapshot, entry, updateRuntimeState); } private static void ApplyConfiguredComponents(GameObject gameObject, PrefabSnapshot snapshot, PrefabConfigurationEntry entry, bool updateRuntimeState, bool allowConditionalMatches) { if (EntryMatches(gameObject, entry, allowConditionalMatches)) { string contextRoot = ((Object)snapshot.Prefab).name + "@" + ((Object)gameObject).name; TryGetCompiledObjectDropRule(entry, out CompiledObjectDropRule compiledRule); ApplyResourceComponents(gameObject, entry, compiledRule, contextRoot, updateRuntimeState); ApplyInteractiveComponents(gameObject, entry, compiledRule, contextRoot, updateRuntimeState); } } private static bool TryGetConfiguredHealth(float configuredHealth, string context, out float normalizedHealth) { if (float.IsNaN(configuredHealth) || float.IsInfinity(configuredHealth) || configuredHealth <= 0f) { WarnInvalidEntry($"Entry '{context}' has invalid health '{configuredHealth}'. Health must be greater than 0."); normalizedHealth = 0f; return false; } normalizedHealth = configuredHealth; return true; } private static bool TryGetConfiguredMinToolTier(int configuredMinToolTier, string context, out int normalizedMinToolTier) { if (configuredMinToolTier < 0) { WarnInvalidEntry($"Entry '{context}' has invalid minToolTier '{configuredMinToolTier}'. minToolTier must be 0 or greater."); normalizedMinToolTier = 0; return false; } normalizedMinToolTier = configuredMinToolTier; return true; } private static void ApplyDestructibleHealth(Destructible destructible, float configuredBaseHealth, bool updateRuntimeState) { float health = destructible.m_health; if (!Mathf.Approximately(health, configuredBaseHealth)) { destructible.m_health = configuredBaseHealth; if (updateRuntimeState) { AdjustSharedHealthZdo(((Component)destructible).gameObject, GetScaledMineHealth(health), GetScaledMineHealth(configuredBaseHealth)); } } } private static void ApplyDestructibleMinToolTier(Destructible destructible, int configuredMinToolTier) { if (destructible.m_minToolTier != configuredMinToolTier) { destructible.m_minToolTier = configuredMinToolTier; } } private static void ApplyDestructibleType(Destructible destructible, DestructibleType configuredType) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) if (destructible.m_destructibleType != configuredType) { destructible.m_destructibleType = configuredType; } } private static void ApplyMineRockHealth(MineRock mineRock, float configuredBaseHealth, bool updateRuntimeState) { float health = mineRock.m_health; mineRock.m_health = configuredBaseHealth; if (updateRuntimeState) { AdjustMineRockAreaHealth(mineRock, GetScaledMineHealth(health), GetScaledMineHealth(configuredBaseHealth)); } } private static void ApplyMineRock5Health(MineRock5 mineRock5, float configuredBaseHealth, bool updateRuntimeState) { float health = mineRock5.m_health; mineRock5.m_health = configuredBaseHealth; if (updateRuntimeState) { AdjustMineRock5AreaHealth(mineRock5, GetScaledMineHealth(health), GetScaledMineHealth(configuredBaseHealth)); } } private static void ApplyTreeBaseHealth(TreeBase treeBase, float configuredBaseHealth, bool updateRuntimeState) { float health = treeBase.m_health; treeBase.m_health = configuredBaseHealth; if (updateRuntimeState) { AdjustSharedHealthZdo(((Component)treeBase).gameObject, Mathf.Max(health, 0.01f), Mathf.Max(configuredBaseHealth, 0.01f)); } } private static void ApplyTreeLogHealth(TreeLog treeLog, float configuredBaseHealth, bool updateRuntimeState) { float health = treeLog.m_health; treeLog.m_health = configuredBaseHealth; if (updateRuntimeState) { AdjustSharedHealthZdo(((Component)treeLog).gameObject, GetScaledMineHealth(health), GetScaledMineHealth(configuredBaseHealth)); } } private static float GetScaledMineHealth(float baseHealth) { if ((Object)(object)Game.instance == (Object)null) { return baseHealth; } return baseHealth + (float)Game.m_worldLevel * Game.instance.m_worldLevelMineHPMultiplier * baseHealth; } private static void AdjustSharedHealthZdo(GameObject gameObject, float oldMaxHealth, float newMaxHealth) { ZNetView component = gameObject.GetComponent(); ZDO val = ((component != null) ? component.GetZDO() : null); if (!((Object)(object)component == (Object)null) && val != null && component.IsOwner()) { float num = RemapHealth(val.GetFloat(ZDOVars.s_health, oldMaxHealth), oldMaxHealth, newMaxHealth); val.Set(ZDOVars.s_health, num); } } private static void AdjustMineRockAreaHealth(MineRock mineRock, float oldMaxHealth, float newMaxHealth) { ZNetView component = ((Component)mineRock).GetComponent(); ZDO val = ((component != null) ? component.GetZDO() : null); if (!((Object)(object)component == (Object)null) && val != null && component.IsOwner()) { int mineRockAreaCount = GetMineRockAreaCount(mineRock); for (int i = 0; i < mineRockAreaCount; i++) { string text = $"Health{i}"; float currentHealth = val.GetFloat(text, oldMaxHealth); val.Set(text, RemapHealth(currentHealth, oldMaxHealth, newMaxHealth)); } } } private static void SetMineRockAreaHealthAbsolute(MineRock mineRock, float configuredAreaHealth) { ZNetView component = ((Component)mineRock).GetComponent(); ZDO val = ((component != null) ? component.GetZDO() : null); if (!((Object)(object)component == (Object)null) && val != null && component.IsOwner()) { int mineRockAreaCount = GetMineRockAreaCount(mineRock); for (int i = 0; i < mineRockAreaCount; i++) { val.Set($"Health{i}", configuredAreaHealth); } } } private static int GetMineRockAreaCount(MineRock mineRock) { return (((Object)(object)mineRock.m_areaRoot != (Object)null) ? mineRock.m_areaRoot.GetComponentsInChildren() : ((Component)mineRock).gameObject.GetComponentsInChildren()).Length; } private static void AdjustMineRock5AreaHealth(MineRock5 mineRock5, float oldMaxHealth, float newMaxHealth) { if (MineRock5HitAreasField == null || MineRock5HitAreaHealthField == null) { return; } ZNetView component = ((Component)mineRock5).GetComponent(); ZDO val = ((component != null) ? component.GetZDO() : null); if ((Object)(object)component == (Object)null || val == null || !component.IsOwner() || !(MineRock5HitAreasField.GetValue(mineRock5) is IList list)) { return; } foreach (object item in list) { if (item != null) { float currentHealth = ((MineRock5HitAreaHealthField.GetValue(item) is float num) ? num : oldMaxHealth); MineRock5HitAreaHealthField.SetValue(item, RemapHealth(currentHealth, oldMaxHealth, newMaxHealth)); } } MineRock5SaveHealthMethod?.Invoke(mineRock5, null); MineRock5UpdateMeshMethod?.Invoke(mineRock5, null); } private static void SetMineRock5AreaHealthAbsolute(MineRock5 mineRock5, float configuredAreaHealth) { if (MineRock5HitAreasField == null || MineRock5HitAreaHealthField == null) { return; } ZNetView component = ((Component)mineRock5).GetComponent(); ZDO val = ((component != null) ? component.GetZDO() : null); if ((Object)(object)component == (Object)null || val == null || !component.IsOwner() || !(MineRock5HitAreasField.GetValue(mineRock5) is IList list)) { return; } foreach (object item in list) { if (item != null) { MineRock5HitAreaHealthField.SetValue(item, configuredAreaHealth); } } MineRock5SaveHealthMethod?.Invoke(mineRock5, null); MineRock5UpdateMeshMethod?.Invoke(mineRock5, null); } private static float RemapHealth(float currentHealth, float oldMaxHealth, float newMaxHealth) { if (newMaxHealth <= 0f || currentHealth <= 0f) { return 0f; } if (oldMaxHealth <= 0f) { return newMaxHealth; } return Mathf.Clamp01(currentHealth / oldMaxHealth) * newMaxHealth; } private static void ApplyPickableDefinition(Pickable pickable, PickableDefinition definition, string context, bool updateRuntimeState) { if (HasPickableDropOverride(definition.Drop)) { if (!string.IsNullOrWhiteSpace(definition.Drop.Item)) { GameObject val = ResolveItemPrefab(definition.Drop.Item, context); if ((Object)(object)val != (Object)null && (Object)(object)pickable.m_itemPrefab != (Object)(object)val) { pickable.m_itemPrefab = val; } } if (definition.Drop.Amount.HasValue && pickable.m_amount != Math.Max(1, definition.Drop.Amount.Value)) { pickable.m_amount = Math.Max(1, definition.Drop.Amount.Value); } if (definition.Drop.MinAmountScaled.HasValue && pickable.m_minAmountScaled != Math.Max(1, definition.Drop.MinAmountScaled.Value)) { pickable.m_minAmountScaled = Math.Max(1, definition.Drop.MinAmountScaled.Value); } if (definition.Drop.DontScale.HasValue && pickable.m_dontScale != definition.Drop.DontScale.Value) { pickable.m_dontScale = definition.Drop.DontScale.Value; } } if (definition.OverrideName != null && pickable.m_overrideName != definition.OverrideName) { pickable.m_overrideName = definition.OverrideName; } } private static void ApplyPickableDefinition(Pickable pickable, CompiledPickableDefinition definition, bool updateRuntimeState) { if (definition.HasItemPrefabOverride && (Object)(object)pickable.m_itemPrefab != (Object)(object)definition.ItemPrefab) { pickable.m_itemPrefab = definition.ItemPrefab; } if (definition.HasAmountOverride && pickable.m_amount != definition.Amount) { pickable.m_amount = definition.Amount; } if (definition.HasMinAmountScaledOverride && pickable.m_minAmountScaled != definition.MinAmountScaled) { pickable.m_minAmountScaled = definition.MinAmountScaled; } if (definition.HasDontScaleOverride && pickable.m_dontScale != definition.DontScale) { pickable.m_dontScale = definition.DontScale; } if (definition.HasOverrideNameOverride && pickable.m_overrideName != definition.OverrideName) { pickable.m_overrideName = definition.OverrideName; } } private static void ApplyPickableItemDefinition(PickableItem pickableItem, PickableItemDefinition definition, string context, bool updateRuntimeState) { bool flag = false; bool forceRandomRefresh = false; bool flag2 = definition.RandomDrops != null; if (definition.RandomDrops == null) { List list = (definition.RandomDrops = new List()); } bool flag3 = definition.RandomDrops.Count > 0; bool flag4 = HasPickableItemDropOverride(definition.Drop); if (flag3 && flag4) { WarnInvalidEntry("Entry '" + context + "' defines both drop and randomDrops. randomDrops take precedence."); } if (flag3) { BuiltRandomPickableItems builtRandomPickableItems = BuildRandomItems(definition.RandomDrops, context); if (!PickableItemRandomItemsEqual(pickableItem.m_randomItemPrefabs, builtRandomPickableItems.Items)) { pickableItem.m_randomItemPrefabs = builtRandomPickableItems.Items; flag = true; forceRandomRefresh = true; } if (!PickableItemRandomWeightsEqual(pickableItem, builtRandomPickableItems.Weights)) { SetPickableItemRandomWeights(pickableItem, builtRandomPickableItems.Weights); flag = true; forceRandomRefresh = true; } if ((Object)(object)pickableItem.m_itemPrefab != (Object)null && !builtRandomPickableItems.Items.Any((RandomItem randomItem) => (Object)(object)randomItem.m_itemPrefab == (Object)(object)pickableItem.m_itemPrefab)) { pickableItem.m_itemPrefab = null; flag = true; forceRandomRefresh = true; } if (builtRandomPickableItems.Items.Length == 0) { if ((Object)(object)pickableItem.m_itemPrefab != (Object)null) { pickableItem.m_itemPrefab = null; flag = true; } if (pickableItem.m_stack != 1) { pickableItem.m_stack = 1; flag = true; } } } else { if (LazyRuntimeState.HasRandomState(pickableItem) && !PickableItemRandomWeightsEqual(pickableItem, Array.Empty())) { ClearPickableItemRandomWeights(pickableItem); flag = true; } if (flag2 && pickableItem.m_randomItemPrefabs.Length != 0) { pickableItem.m_randomItemPrefabs = Array.Empty(); flag = true; } if (flag4) { if (!string.IsNullOrWhiteSpace(definition.Drop?.Item)) { GameObject val = ResolveItemPrefab(definition.Drop.Item, context); if ((Object)(object)val != (Object)null) { ItemDrop component = val.GetComponent(); if ((Object)(object)pickableItem.m_itemPrefab != (Object)(object)component) { pickableItem.m_itemPrefab = component; flag = true; } } } PickableItemDropDefinition? drop = definition.Drop; if (drop != null && drop.Stack.HasValue && pickableItem.m_stack != Math.Max(1, definition.Drop.Stack.Value)) { pickableItem.m_stack = Math.Max(1, definition.Drop.Stack.Value); flag = true; } } } if (updateRuntimeState && flag) { bool useRandomItems = flag3 && pickableItem.m_randomItemPrefabs.Length != 0; UpdatePickableItemRuntimeState(pickableItem, useRandomItems, forceRandomRefresh); } } private static void ApplyPickableItemDefinition(PickableItem pickableItem, CompiledPickableItemDefinition definition, bool updateRuntimeState) { bool flag = false; bool forceRandomRefresh = false; bool flag2 = definition.RandomItems.Length != 0; if (flag2) { if (!PickableItemRandomItemsEqual(pickableItem.m_randomItemPrefabs, definition.RandomItems)) { RandomItem[] array = (RandomItem[])(object)new RandomItem[definition.RandomItems.Length]; Array.Copy(definition.RandomItems, array, definition.RandomItems.Length); pickableItem.m_randomItemPrefabs = array; flag = true; forceRandomRefresh = true; } if (!PickableItemRandomWeightsEqual(pickableItem, definition.RandomWeights)) { SetPickableItemRandomWeights(pickableItem, definition.RandomWeights); flag = true; forceRandomRefresh = true; } if ((Object)(object)pickableItem.m_itemPrefab != (Object)null && !definition.RandomItems.Any((RandomItem randomItem) => (Object)(object)randomItem.m_itemPrefab == (Object)(object)pickableItem.m_itemPrefab)) { pickableItem.m_itemPrefab = null; flag = true; forceRandomRefresh = true; } if (definition.RandomItems.Length == 0) { if ((Object)(object)pickableItem.m_itemPrefab != (Object)null) { pickableItem.m_itemPrefab = null; flag = true; } if (pickableItem.m_stack != 1) { pickableItem.m_stack = 1; flag = true; } } } else { if (LazyRuntimeState.HasRandomState(pickableItem) && !PickableItemRandomWeightsEqual(pickableItem, Array.Empty())) { ClearPickableItemRandomWeights(pickableItem); flag = true; } if (definition.HasRandomOverride && pickableItem.m_randomItemPrefabs.Length != 0) { pickableItem.m_randomItemPrefabs = Array.Empty(); flag = true; } if (definition.HasFixedDrop) { if (definition.HasFixedItemOverride && (Object)(object)pickableItem.m_itemPrefab != (Object)(object)definition.FixedItemPrefab) { pickableItem.m_itemPrefab = definition.FixedItemPrefab; flag = true; } if (definition.HasFixedStackOverride && pickableItem.m_stack != definition.FixedStack) { pickableItem.m_stack = definition.FixedStack; flag = true; } } } if (updateRuntimeState && flag) { UpdatePickableItemRuntimeState(pickableItem, flag2 && pickableItem.m_randomItemPrefabs.Length != 0, forceRandomRefresh); } } private static void ApplyDestructibleDefinition(Destructible destructible, DestructibleDefinition definition, string context, bool includeSpawnWhenDestroyed) { //IL_001e: Unknown result type (might be due to invalid IL or missing references) if (!string.IsNullOrWhiteSpace(definition.DestructibleType)) { if (Enum.TryParse(definition.DestructibleType, ignoreCase: true, out DestructibleType result)) { ApplyDestructibleType(destructible, result); } else { WarnInvalidEntry("Entry '" + context + "' has invalid destructibleType '" + definition.DestructibleType + "'."); } } if (includeSpawnWhenDestroyed && !string.IsNullOrWhiteSpace(definition.SpawnWhenDestroyed)) { destructible.m_spawnWhenDestroyed = ResolveSpawnPrefab(definition.SpawnWhenDestroyed, context); } } private static GameObject? ResolveSpawnPrefab(string prefabName, string context) { 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("Entry '" + context + "' references unknown spawn prefab '" + text + "'."); } return val; } private static DropTable BuildEffectiveDropTable(DropTable? snapshotTable, IEnumerable matchingPayloads, string context) { List list = (matchingPayloads ?? Enumerable.Empty()).Where(HasDropTableOverride).ToList(); if (list.Count == 0) { if (snapshotTable == null) { return CreateDefaultDropTable(); } return CloneDropTable(snapshotTable); } DropTable val = ((snapshotTable != null) ? CloneDropTable(snapshotTable) : CreateDefaultDropTable()); val.m_drops = new List(); HashSet seenFingerprints = new HashSet(StringComparer.Ordinal); foreach (DropTablePayloadDefinition item in list) { ApplyDropTableScalarOverrides(val, item, resetToDefaultsWhenUnset: false); AppendDropTableRows(val.m_drops, item.Drops, context, seenFingerprints); } return val; } private static DropTable BuildEffectiveDropTable(DropTable? snapshotTable, IEnumerable matchingPayloads) { List list = (matchingPayloads ?? Enumerable.Empty()).ToList(); if (list.Count == 0) { if (snapshotTable == null) { return CreateDefaultDropTable(); } return CloneDropTable(snapshotTable); } DropTable val = ((snapshotTable != null) ? CloneDropTable(snapshotTable) : CreateDefaultDropTable()); val.m_drops = new List(); HashSet seenFingerprints = new HashSet(StringComparer.Ordinal); foreach (CompiledDropTablePayload item in list) { ApplyDropTableScalarOverrides(val, item); AppendDropTableRows(val.m_drops, item.Drops, seenFingerprints); } return val; } private static DropTable BuildEffectiveDropTable(StaticCompiledDropTableTemplate template, IEnumerable matchingPayloads) { List list = (matchingPayloads ?? Enumerable.Empty()).ToList(); if (list.Count == 0) { return CloneDropTable(template.Template); } DropTable val = CloneDropTable(template.Template); HashSet seenFingerprints = new HashSet(template.Fingerprints, StringComparer.Ordinal); foreach (CompiledDropTablePayload item in list) { ApplyDropTableScalarOverrides(val, item); AppendDropTableRows(val.m_drops, item.Drops, seenFingerprints); } return val; } private static DropTable CreateDefaultDropTable() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Expected O, but got Unknown return new DropTable { m_dropMin = 1, m_dropMax = 1, m_dropChance = 1f, m_oneOfEach = false, m_drops = new List() }; } private static void ApplyDropTableScalarOverrides(DropTable dropTable, DropTablePayloadDefinition definition, bool resetToDefaultsWhenUnset) { if (definition.DropMin.HasValue || definition.DropMax.HasValue) { int num = Math.Max(0, definition.DropMin ?? 1); int dropMax = Math.Max(num, definition.DropMax ?? definition.DropMin ?? 1); dropTable.m_dropMin = num; dropTable.m_dropMax = dropMax; } else if (resetToDefaultsWhenUnset) { dropTable.m_dropMin = 1; dropTable.m_dropMax = 1; } if (definition.DropChance.HasValue) { dropTable.m_dropChance = Mathf.Clamp01(definition.DropChance.Value); } else if (resetToDefaultsWhenUnset) { dropTable.m_dropChance = 1f; } if (definition.OneOfEach.HasValue) { dropTable.m_oneOfEach = definition.OneOfEach.Value; } else if (resetToDefaultsWhenUnset) { dropTable.m_oneOfEach = false; } } private static void ApplyDropTableScalarOverrides(DropTable dropTable, CompiledDropTablePayload definition) { if (definition.HasDropRangeOverride) { dropTable.m_dropMin = definition.DropMin; dropTable.m_dropMax = definition.DropMax; } if (definition.HasDropChanceOverride) { dropTable.m_dropChance = definition.DropChance; } if (definition.HasOneOfEachOverride) { dropTable.m_oneOfEach = definition.OneOfEach; } } private static void AppendDropTableRows(List target, IEnumerable? definitions, string context, HashSet? seenFingerprints = null) { //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_0134: Unknown result type (might be due to invalid IL or missing references) foreach (DropEntryDefinition item2 in definitions ?? Enumerable.Empty()) { string text = (item2.Item ?? "").Trim(); if (text.Length == 0) { WarnInvalidEntry("Entry '" + context + "' contains a drop without an item name."); continue; } GameObject val = ResolveItemPrefab(text, context); if (!((Object)(object)val == (Object)null)) { string item = BuildDropRowFingerprint(item2); if (seenFingerprints == null || seenFingerprints.Add(item)) { target.Add(new DropData { m_item = val, m_stackMin = Math.Max(1, item2.StackMin ?? 1), m_stackMax = Math.Max(Math.Max(1, item2.StackMin ?? 1), item2.StackMax ?? item2.StackMin ?? 1), m_weight = Mathf.Max(0f, item2.Weight ?? 1f), m_dontScale = (item2.DontScale == true) }); } } } } private static void AppendDropTableRows(List target, IEnumerable definitions, HashSet? seenFingerprints = null) { //IL_002d: 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) foreach (CompiledDropTableRow item in definitions ?? Enumerable.Empty()) { if (seenFingerprints == null || seenFingerprints.Add(item.Fingerprint)) { target.Add(new DropData { m_item = item.ItemPrefab, m_stackMin = item.StackMin, m_stackMax = item.StackMax, m_weight = item.Weight, m_dontScale = item.DontScale }); } } } private static string BuildDropRowFingerprint(DropEntryDefinition definition) { return NetworkPayloadSyncSupport.ComputeObjectDropRowSignature(new DropEntryDefinition { Item = (definition.Item ?? "").Trim(), StackMin = Math.Max(1, definition.StackMin ?? 1), StackMax = Math.Max(Math.Max(1, definition.StackMin ?? 1), definition.StackMax ?? definition.StackMin ?? 1), Weight = Mathf.Max(0f, definition.Weight ?? 1f), DontScale = (definition.DontScale == true) }); } private static GameObject? ResolveItemPrefab(string itemName, string context) { string text = (itemName ?? "").Trim(); if (text.Length == 0) { WarnInvalidEntry("Entry '" + context + "' references an empty item prefab name."); 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("Entry '" + context + "' references unknown item prefab '" + text + "'."); return null; } ItemDrop val2 = default(ItemDrop); if (!val.TryGetComponent(ref val2)) { WarnInvalidEntry("Entry '" + context + "' references '" + text + "', but it is not an item prefab."); return null; } return val; } private static DropTable CloneDropTable(DropTable source) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Expected O, but got Unknown //IL_005c: 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_006a: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_008c: 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_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Unknown result type (might be due to invalid IL or missing references) DropTable val = new DropTable { m_dropMin = source.m_dropMin, m_dropMax = source.m_dropMax, m_dropChance = source.m_dropChance, m_oneOfEach = source.m_oneOfEach, m_drops = new List(source.m_drops.Count) }; foreach (DropData drop in source.m_drops) { val.m_drops.Add(new DropData { m_item = drop.m_item, m_stackMin = drop.m_stackMin, m_stackMax = drop.m_stackMax, m_weight = drop.m_weight, m_dontScale = drop.m_dontScale }); } return val; } private 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; } string name = ((Object)gameObject).name; if (name.EndsWith("(Clone)", StringComparison.Ordinal)) { string text = name; int length = "(Clone)".Length; return text.Substring(0, text.Length - length).TrimEnd(); } return name; } private static void RunApplyCoordinator(int gameDataSignature, bool domainEnabled, Dictionary currentEntrySignatures, bool queueLiveReconcile) { ObjectDesiredState objectDesiredState = BuildObjectDesiredState(gameDataSignature, domainEnabled, currentEntrySignatures, queueLiveReconcile); StandardBaselineDesiredStateCoordinator.Run(objectDesiredState.ApplyPlan, objectDesiredState, ObjectApplyOperations.Instance); } private static ObjectDesiredState BuildObjectDesiredState(int gameDataSignature, bool domainEnabled, Dictionary currentEntrySignatures, bool queueLiveReconcile) { StandardDomainApplyPlan applyPlan = StandardDomainApplySupport.BuildPlan(_lastAppliedGameDataSignature, gameDataSignature, _lastAppliedDomainEnabled, domainEnabled, _lastAppliedEntrySignaturesByPrefab, currentEntrySignatures, EmptyEntrySignatures, canUseTargetedLiveReload: true); HashSet dirtyKeys = applyPlan.DirtyKeys; HashSet liveDirtyPrefabs = ((dirtyKeys != null) ? FilterPrefabsRequiringLiveReconcile(dirtyKeys) : null); EnsureRuntimeDropConfigurationState(); return new ObjectDesiredState { GameDataSignature = gameDataSignature, ApplyPlan = applyPlan, CurrentEntrySignatures = currentEntrySignatures, RuntimeConfigurationState = DropRuntimeState.Configuration, DomainEnabled = domainEnabled, QueueLiveReconcile = queueLiveReconcile, DirtyPrefabs = dirtyKeys, LiveDirtyPrefabs = liveDirtyPrefabs, NeedsLiveReload = ((applyPlan.PreviousDomainEnabled && FilterPrefabsRequiringLiveReconcile(_lastAppliedEntrySignaturesByPrefab.Keys).Count > 0) || (domainEnabled && FilterPrefabsRequiringLiveReconcile(currentEntrySignatures.Keys).Count > 0)) }; } private static void RestoreObjectStaticBaseline(ObjectDesiredState desiredState) { RestoreSnapshots(desiredState.DirtyPrefabs); } private static void ValidateObjectDesiredState(ObjectDesiredState desiredState) { ValidateConfiguredPrefabs(); } private static void ApplyObjectDesiredStateToStaticBaseline(ObjectDesiredState desiredState) { ApplyDesiredStateToPrefabs(desiredState); } private static void ApplyObjectDesiredStateToLive(ObjectDesiredState desiredState) { ApplyDesiredStateToLiveObjects(desiredState); } private static bool CanUseGroupConditionalApplyPlan(ConditionsDefinition? conditions) { if (!DropConditionEvaluator.HasConditions(conditions)) { return true; } if (conditions != null && !DropConditionEvaluator.HasDynamicConditions(conditions) && !conditions.InForest.HasValue) { FloatRangeDefinition? distanceFromCenter = conditions.DistanceFromCenter; if ((distanceFromCenter == null || !distanceFromCenter.HasValues()) && !conditions.MinDistanceFromCenter.HasValue && !conditions.MaxDistanceFromCenter.HasValue) { FloatRangeDefinition? altitude = conditions.Altitude; if ((altitude == null || !altitude.HasValues()) && !conditions.MinAltitude.HasValue) { return !conditions.MaxAltitude.HasValue; } } } return false; } private static bool TryGetGroupConditionalApplyPlan(GameObject gameObject, PrefabSnapshot snapshot, IReadOnlyCollection entries, out GroupConditionalApplyPlan? plan) { plan = null; if (entries.Count == 0) { return false; } bool flag = false; bool flag2 = false; bool flag3 = false; bool flag4 = false; foreach (PrefabConfigurationEntry entry in entries) { if (CanUseGroupConditionalApplyPlan(entry.Conditions)) { flag = true; ConditionsDefinition conditions = entry.Conditions; flag2 |= conditions != null && conditions.Locations?.Count > 0; flag3 |= (conditions != null && conditions.ResolvedBiomeMask.HasValue) || (conditions != null && conditions.Biomes?.Count > 0); flag4 |= conditions?.InDungeon.HasValue ?? false; } } if (!flag) { return false; } string groupKey = BuildObjectReconcileGroupKey(gameObject, ((Object)snapshot.Prefab).name); StaticConditionContextSnapshot orBuildStaticConditionContextSnapshot = GetOrBuildStaticConditionContextSnapshot(gameObject, flag2, flag3, flag4); string resolvedLocationName = (flag2 ? orBuildStaticConditionContextSnapshot.ResolvedLocationName : null); string cacheKey = BuildGroupConditionalApplyPlanCacheKey(groupKey, flag2, flag3, flag4, orBuildStaticConditionContextSnapshot); if (ConditionPlanCacheState.TryGetGroupConditionalApplyPlan(cacheKey, out GroupConditionalApplyPlan plan2)) { plan = plan2; return true; } EnsureRuntimeDropConfigurationState(); DropRuntimeState.Configuration.PlansByPrefab.TryGetValue(((Object)snapshot.Prefab).name, out CompiledObjectPrefabPlan value); List list = value?.Rules; GroupConditionalApplyPlan groupConditionalApplyPlan = new GroupConditionalApplyPlan(); foreach (PrefabConfigurationEntry entry2 in entries) { if (CanUseGroupConditionalApplyPlan(entry2.Conditions)) { groupConditionalApplyPlan.EligibleEntries.Add(entry2); if (!DropConditionEvaluator.HasConditions(entry2.Conditions) || DropConditionEvaluator.AreStaticConditionsSatisfied(gameObject, entry2.Conditions, resolvedLocationName)) { groupConditionalApplyPlan.MatchingEntries.Add(entry2); } } } if (list != null) { HashSet hashSet = new HashSet(groupConditionalApplyPlan.MatchingEntries); foreach (CompiledObjectDropRule item in list) { if (groupConditionalApplyPlan.EligibleEntries.Contains(item.Entry)) { groupConditionalApplyPlan.EligibleCompiledRules.Add(item); if (hashSet.Contains(item.Entry)) { groupConditionalApplyPlan.MatchingCompiledRules.Add(item); } } } } ConditionPlanCacheState.StoreGroupConditionalApplyPlan(cacheKey, groupConditionalApplyPlan); plan = groupConditionalApplyPlan; return true; } private static StaticConditionContextSnapshot GetOrBuildStaticConditionContextSnapshot(GameObject gameObject, bool usesLocation, bool usesBiome, bool usesInDungeon) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0019: 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_006e: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) int instanceID = ((Object)gameObject).GetInstanceID(); Vector3 position = gameObject.transform.position; if (ConditionPlanCacheState.TryGetStaticConditionContext(instanceID, position, out StaticConditionContextSnapshot snapshot)) { return snapshot; } snapshot = new StaticConditionContextSnapshot { Position = position }; if (usesLocation) { snapshot.ResolvedLocationName = DropConditionEvaluator.GetResolvedLocationNameForConditions(gameObject) ?? ""; } if (usesBiome) { StaticConditionContextSnapshot staticConditionContextSnapshot = snapshot; WorldGenerator instance = WorldGenerator.instance; staticConditionContextSnapshot.Biome = ((instance != null) ? instance.GetBiome(position) : Heightmap.FindBiome(position)); } if (usesInDungeon) { snapshot.InDungeon = Character.InInterior(position); } return ConditionPlanCacheState.StoreStaticConditionContext(instanceID, snapshot); } private static string BuildGroupConditionalApplyPlanCacheKey(string groupKey, bool usesLocation, bool usesBiome, bool usesInDungeon, StaticConditionContextSnapshot conditionContext) { //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Expected I4, but got Unknown StringBuilder stringBuilder = new StringBuilder(groupKey.Length + 64); stringBuilder.Append(groupKey); if (usesLocation) { stringBuilder.Append("|loc:"); stringBuilder.Append(conditionContext.ResolvedLocationName); } if (usesBiome) { stringBuilder.Append("|bio:"); stringBuilder.Append((int)conditionContext.Biome); } if (usesInDungeon) { stringBuilder.Append("|dgn:"); stringBuilder.Append(conditionContext.InDungeon ? '1' : '0'); } return stringBuilder.ToString(); } private static void RemovePendingObjectConditionPlanState(int instanceId) { ConditionPlanCacheState.InvalidateStaticConditionContextForInstance(instanceId); } private static void ClearLiveObjectConditionCaches() { ConditionPlanCacheState.Clear(); } private static void WarnMissingComponent(string key, string componentName) { if (MissingComponentWarnings.Add(key)) { DropNSpawnPlugin.DropNSpawnLogger.LogWarning((object)("Prefab configuration references " + componentName + ", but the component was not found on '" + key + "'.")); } } private static void WarnInvalidEntry(string message) { InvalidEntryWarnings.Warn(message); } private static InvalidEntryDiagnostics.SuppressionScope BeginInvalidEntryWarningSuppressionForSyncedClientBuild(string sourceName) { return InvalidEntryWarnings.BeginSuppressionForSyncedClientBuild(sourceName); } private static void LogPartiallyAcceptedLocalConfiguration(int parsedEntryCount, int acceptedEntryCount, List warnings) { DropNSpawnPlugin.DropNSpawnLogger.LogWarning((object)$"Loaded {acceptedEntryCount} object override entries from {parsedEntryCount} parsed entries. Invalid entries were skipped."); foreach (string item in warnings.Where((string message) => !string.IsNullOrWhiteSpace(message)).Distinct(StringComparer.OrdinalIgnoreCase)) { DropNSpawnPlugin.DropNSpawnLogger.LogWarning((object)item); } } private static void LogPartiallyAcceptedLocalConfigurationHook(int parsedEntryCount, int acceptedEntryCount, IEnumerable warnings) { LogPartiallyAcceptedLocalConfiguration(parsedEntryCount, acceptedEntryCount, warnings.ToList()); } private static void LogLocalConfigurationLoaded(int acceptedEntryCount, int loadedFileCount) { DropNSpawnPlugin.DropNSpawnLogger.LogInfo((object)$"Loaded {acceptedEntryCount} object override entries from {loadedFileCount} override file(s)."); } private static void OnSourceOfTruthPayloadUnchanged() { if (!NetworkPayloadSyncSupport.IsPayloadCurrent(Descriptor, RuntimeState.ConfigurationSignature)) { ConfigurationDomainHost.PublishSyncedPayload(DropNSpawnPlugin.IsSourceOfTruth, Descriptor, RuntimeState.Configuration, RuntimeState.ConfigurationSignature); } } private static void LogSyncedObjectConfigurationLoaded(string payloadToken, int acceptedEntryCount) { DropNSpawnPlugin.DropNSpawnLogger.LogInfo((object)$"Loaded {acceptedEntryCount} synchronized object configuration(s) from the server."); } private static void LogSyncedObjectConfigurationFailure(string payloadToken, Exception ex) { DropNSpawnPlugin.DropNSpawnLogger.LogError((object)$"Failed to deserialize synchronized object payload DTO. {ex}"); } private static string CreateConfigurationContext(PrefabConfigurationEntry entry) { return (string.IsNullOrWhiteSpace(entry.Prefab) ? "" : entry.Prefab) + " @ " + DescribeEntrySource(entry); } private static string DescribeEntrySource(PrefabConfigurationEntry entry) { string text = DescribeEntrySource(entry.SourcePath); if (entry.SourceLine <= 0) { return text; } return text + ":" + entry.SourceLine.ToString(CultureInfo.InvariantCulture); } private static string DescribeEntrySource(string? sourcePath) { if (!string.IsNullOrWhiteSpace(sourcePath)) { return sourcePath; } return "unknown source"; } private static string FormatYamlExceptionLocation(Exception ex) { if (!(ex is YamlException { Start: var start } ex2) || start.Line <= 0) { return ""; } return ":" + ex2.Start.Line.ToString(CultureInfo.InvariantCulture); } private static string BuildCompiledObjectDropContext(PrefabConfigurationEntry entry, string componentName) { string text = (string.IsNullOrWhiteSpace(entry.Prefab) ? "" : entry.Prefab); string text2 = (string.IsNullOrWhiteSpace(entry.RuleId) ? "" : entry.RuleId); return text + "/" + componentName + "/" + text2 + "@" + DescribeEntrySource(entry); } private static void RestoreInteractiveComponents(GameObject gameObject, PrefabSnapshot snapshot, PrefabConfigurationEntry entry, bool updateRuntimeState) { Container val = default(Container); if (UsesLiveDropTableReconcile(LiveObjectComponentKind.Container) && HasDropTableOverride(entry.Container) && gameObject.TryGetComponent(ref val) && snapshot.Container != null) { val.m_defaultItems = CloneDropTable(snapshot.Container); } Pickable val2 = default(Pickable); if (HasPickableOverride(entry.Pickable) && gameObject.TryGetComponent(ref val2) && snapshot.Pickable != null) { val2.m_itemPrefab = snapshot.Pickable.ItemPrefab; val2.m_amount = snapshot.Pickable.Amount; val2.m_minAmountScaled = snapshot.Pickable.MinAmountScaled; val2.m_dontScale = snapshot.Pickable.DontScale; val2.m_overrideName = snapshot.Pickable.OverrideName; val2.m_extraDrops = CloneDropTable(snapshot.Pickable.ExtraDrops); } PickableItem pickableItem = default(PickableItem); if (HasPickableItemOverride(entry.PickableItem) && gameObject.TryGetComponent(ref pickableItem) && snapshot.PickableItem != null) { RestorePickableItem(pickableItem, snapshot.PickableItem, updateRuntimeState); } Fish val3 = default(Fish); if (HasFishOverride(entry.Fish) && gameObject.TryGetComponent(ref val3) && snapshot.Fish != null) { val3.m_extraDrops = CloneDropTable(snapshot.Fish.ExtraDrops); } } private static void ApplyInteractiveComponents(GameObject gameObject, PrefabConfigurationEntry entry, CompiledObjectDropRule? compiledRule, string contextRoot, bool updateRuntimeState) { Container val = default(Container); if (HasDropTableOverride(entry.Container) && !gameObject.TryGetComponent(ref val)) { WarnMissingComponent(contextRoot + "/Container", "Container"); } if (HasPickableOverride(entry.Pickable)) { Pickable pickable = default(Pickable); if (!gameObject.TryGetComponent(ref pickable)) { WarnMissingComponent(contextRoot + "/Pickable", "Pickable"); } else if (compiledRule?.Pickable != null) { ApplyPickableDefinition(pickable, compiledRule.Pickable, updateRuntimeState); } else { ApplyPickableDefinition(pickable, entry.Pickable, contextRoot + "/Pickable", updateRuntimeState); } } if (HasPickableItemOverride(entry.PickableItem)) { PickableItem pickableItem = default(PickableItem); if (!gameObject.TryGetComponent(ref pickableItem)) { WarnMissingComponent(contextRoot + "/PickableItem", "PickableItem"); } else if (compiledRule?.PickableItem != null) { ApplyPickableItemDefinition(pickableItem, compiledRule.PickableItem, updateRuntimeState); } else { ApplyPickableItemDefinition(pickableItem, entry.PickableItem, contextRoot + "/PickableItem", updateRuntimeState); } } } internal static void ApplyLazyMineRockScalarsIfNeeded(MineRock mineRock) { lock (Sync) { if (TryResolveLazyDamageableScalars(((Component)mineRock).gameObject, LiveObjectComponentKind.MineRock, out CompiledDamageableScalarDefinition resolvedScalars, out int signature)) { ApplyLazyMineRockScalars(mineRock, resolvedScalars, signature); } } } internal static void ApplyLazyDestructibleScalarsIfNeeded(Destructible destructible) { lock (Sync) { if (TryResolveLazyDestructibleScalars(((Component)destructible).gameObject, out CompiledDestructibleComponentDefinition resolvedDefinition, out int signature)) { ApplyLazyDestructibleScalars(destructible, resolvedDefinition, signature); } } } internal static void ApplyLazyDestructibleTypeIfNeeded(Destructible destructible) { //IL_0022: Unknown result type (might be due to invalid IL or missing references) lock (Sync) { if (TryResolveLazyDestructibleType(((Component)destructible).gameObject, out var resolvedType)) { ApplyDestructibleType(destructible, resolvedType); } } } internal static void ApplyLazyMineRock5ScalarsIfNeeded(MineRock5 mineRock5) { lock (Sync) { if (TryResolveLazyDamageableScalars(((Component)mineRock5).gameObject, LiveObjectComponentKind.MineRock5, out CompiledDamageableScalarDefinition resolvedScalars, out int signature)) { ApplyLazyMineRock5Scalars(mineRock5, resolvedScalars, signature); } } } internal static void ApplyLazyTreeBaseScalarsIfNeeded(TreeBase treeBase) { lock (Sync) { if (TryResolveLazyDamageableScalars(((Component)treeBase).gameObject, LiveObjectComponentKind.TreeBase, out CompiledDamageableScalarDefinition resolvedScalars, out int signature)) { ApplyLazyTreeBaseScalars(treeBase, resolvedScalars, signature); } } } internal static void ApplyLazyTreeLogScalarsIfNeeded(TreeLog treeLog) { lock (Sync) { if (TryResolveLazyDamageableScalars(((Component)treeLog).gameObject, LiveObjectComponentKind.TreeLog, out CompiledDamageableScalarDefinition resolvedScalars, out int signature)) { ApplyLazyTreeLogScalars(treeLog, resolvedScalars, signature); } } } private static bool TryResolveLazyDamageableScalars(GameObject gameObject, LiveObjectComponentKind componentKind, out CompiledDamageableScalarDefinition resolvedScalars, out int signature) { resolvedScalars = null; signature = 0; if (!TryGetConditionalDropContext(gameObject, out string _, out PrefabSnapshot _, out List compiledRules)) { return false; } CompiledDamageableScalarDefinition compiledDamageableScalarDefinition = new CompiledDamageableScalarDefinition(); foreach (CompiledObjectDropRule item in compiledRules) { if (!CanUseLazyDamageableScalarFastPath(item.Entry, componentKind) || !EntryMatches(gameObject, item.Entry, allowConditionalMatches: true)) { continue; } CompiledDamageableScalarDefinition damageableScalarDefinition = GetDamageableScalarDefinition(item, componentKind); if (damageableScalarDefinition != null) { if (damageableScalarDefinition.HasHealthOverride) { compiledDamageableScalarDefinition.HasHealthOverride = true; compiledDamageableScalarDefinition.Health = damageableScalarDefinition.Health; } if (damageableScalarDefinition.HasMinToolTierOverride) { compiledDamageableScalarDefinition.HasMinToolTierOverride = true; compiledDamageableScalarDefinition.MinToolTier = damageableScalarDefinition.MinToolTier; } } } if (!compiledDamageableScalarDefinition.HasHealthOverride && !compiledDamageableScalarDefinition.HasMinToolTierOverride) { return false; } resolvedScalars = compiledDamageableScalarDefinition; signature = ComputeLazyDamageableScalarSignature(compiledDamageableScalarDefinition); return true; } private static bool TryResolveLazyDestructibleScalars(GameObject gameObject, out CompiledDestructibleComponentDefinition resolvedDefinition, out int signature) { resolvedDefinition = null; signature = 0; if (!TryGetConditionalDropContext(gameObject, out string _, out PrefabSnapshot _, out List compiledRules)) { return false; } CompiledDestructibleComponentDefinition compiledDestructibleComponentDefinition = new CompiledDestructibleComponentDefinition(); foreach (CompiledObjectDropRule item in compiledRules) { if (item.Destructible != null && CanUseLazyDestructibleScalarFastPath(item.Entry) && EntryMatches(gameObject, item.Entry, allowConditionalMatches: true)) { if (item.Destructible.HasHealthOverride) { compiledDestructibleComponentDefinition.HasHealthOverride = true; compiledDestructibleComponentDefinition.Health = item.Destructible.Health; } if (item.Destructible.HasMinToolTierOverride) { compiledDestructibleComponentDefinition.HasMinToolTierOverride = true; compiledDestructibleComponentDefinition.MinToolTier = item.Destructible.MinToolTier; } } } if (!compiledDestructibleComponentDefinition.HasHealthOverride && !compiledDestructibleComponentDefinition.HasMinToolTierOverride) { return false; } resolvedDefinition = compiledDestructibleComponentDefinition; signature = ComputeLazyDestructibleScalarSignature(compiledDestructibleComponentDefinition); return true; } private static bool TryResolveLazyDestructibleType(GameObject gameObject, out DestructibleType resolvedType) { //IL_0056: 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_00dc: Unknown result type (might be due to invalid IL or missing references) //IL_00df: Expected I4, but got Unknown //IL_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: Unknown result type (might be due to invalid IL or missing references) resolvedType = (DestructibleType)1; if (!TryGetConditionalContext(gameObject, out string prefabName, out PrefabSnapshot snapshot, out List _) || snapshot.Destructible == null) { return false; } EnsureRuntimeDropConfigurationState(); List rules; if (!DropRuntimeState.Configuration.PlansByPrefab.TryGetValue(prefabName, out CompiledObjectPrefabPlan value) || (rules = value.Rules) == null || rules.Count == 0) { return false; } bool flag = false; DestructibleType destructibleType = snapshot.Destructible.DestructibleType; foreach (CompiledObjectDropRule item in rules) { CompiledDestructibleComponentDefinition? destructible = item.Destructible; if (destructible != null && destructible.HasDestructibleTypeOverride) { if (!CanUseLazyDestructibleScalarFastPath(item.Entry)) { return false; } flag = true; if (EntryMatches(gameObject, item.Entry, allowConditionalMatches: true)) { destructibleType = item.Destructible.DestructibleType; } } } if (!flag) { return false; } resolvedType = (DestructibleType)(int)destructibleType; return true; } private static CompiledDamageableScalarDefinition? GetDamageableScalarDefinition(CompiledObjectDropRule compiledRule, LiveObjectComponentKind componentKind) { return componentKind switch { LiveObjectComponentKind.MineRock => compiledRule.MineRockScalars, LiveObjectComponentKind.MineRock5 => compiledRule.MineRock5Scalars, LiveObjectComponentKind.TreeBase => compiledRule.TreeBaseScalars, LiveObjectComponentKind.TreeLog => compiledRule.TreeLogScalars, _ => null, }; } private static int ComputeLazyDamageableScalarSignature(CompiledDamageableScalarDefinition scalars) { return (((17 * 31 + (scalars.HasHealthOverride ? 1 : 0)) * 31 + (scalars.HasHealthOverride ? BitConverter.SingleToInt32Bits(scalars.Health) : 0)) * 31 + (scalars.HasMinToolTierOverride ? 1 : 0)) * 31 + (scalars.HasMinToolTierOverride ? scalars.MinToolTier : 0); } private static int ComputeLazyDestructibleScalarSignature(CompiledDestructibleComponentDefinition definition) { return (((17 * 31 + (definition.HasHealthOverride ? 1 : 0)) * 31 + (definition.HasHealthOverride ? BitConverter.SingleToInt32Bits(definition.Health) : 0)) * 31 + (definition.HasMinToolTierOverride ? 1 : 0)) * 31 + (definition.HasMinToolTierOverride ? definition.MinToolTier : 0); } private static void ApplyLazyTreeBaseScalars(TreeBase treeBase, CompiledDamageableScalarDefinition scalars, int signature) { ZNetView component = ((Component)treeBase).GetComponent(); ZDO val = ((component != null) ? component.GetZDO() : null); bool flag = ((val != null) ? val.GetInt(TreeBaseLazyScalarSignatureKey, int.MinValue) : int.MinValue) == signature; bool num = !scalars.HasHealthOverride || Mathf.Approximately(treeBase.m_health, scalars.Health); bool flag2 = !scalars.HasMinToolTierOverride || treeBase.m_minToolTier == scalars.MinToolTier; if (num && flag2 && (flag || (Object)(object)component == (Object)null || val == null || !component.IsOwner())) { return; } if (scalars.HasHealthOverride) { treeBase.m_health = scalars.Health; } if (scalars.HasMinToolTierOverride) { treeBase.m_minToolTier = scalars.MinToolTier; } if (!((Object)(object)component == (Object)null || val == null || !component.IsOwner() || flag)) { if (scalars.HasHealthOverride) { val.Set(ZDOVars.s_health, Mathf.Max(scalars.Health, 0.01f)); } val.Set(TreeBaseLazyScalarSignatureKey, signature, false); } } private static void ApplyLazyTreeLogScalars(TreeLog treeLog, CompiledDamageableScalarDefinition scalars, int signature) { ZNetView component = ((Component)treeLog).GetComponent(); ZDO val = ((component != null) ? component.GetZDO() : null); bool flag = ((val != null) ? val.GetInt(TreeLogLazyScalarSignatureKey, int.MinValue) : int.MinValue) == signature; bool num = !scalars.HasHealthOverride || Mathf.Approximately(treeLog.m_health, scalars.Health); bool flag2 = !scalars.HasMinToolTierOverride || treeLog.m_minToolTier == scalars.MinToolTier; if (num && flag2 && (flag || (Object)(object)component == (Object)null || val == null || !component.IsOwner())) { return; } if (scalars.HasHealthOverride) { treeLog.m_health = scalars.Health; } if (scalars.HasMinToolTierOverride) { treeLog.m_minToolTier = scalars.MinToolTier; } if (!((Object)(object)component == (Object)null || val == null || !component.IsOwner() || flag)) { if (scalars.HasHealthOverride) { val.Set(ZDOVars.s_health, GetScaledMineHealth(Mathf.Max(scalars.Health, 0.01f))); } val.Set(TreeLogLazyScalarSignatureKey, signature, false); } } private static void ApplyLazyMineRockScalars(MineRock mineRock, CompiledDamageableScalarDefinition scalars, int signature) { ZNetView component = ((Component)mineRock).GetComponent(); ZDO val = ((component != null) ? component.GetZDO() : null); bool flag = ((val != null) ? val.GetInt(MineRockLazyScalarSignatureKey, int.MinValue) : int.MinValue) == signature; bool num = !scalars.HasHealthOverride || Mathf.Approximately(mineRock.m_health, scalars.Health); bool flag2 = !scalars.HasMinToolTierOverride || mineRock.m_minToolTier == scalars.MinToolTier; if (num && flag2 && (flag || (Object)(object)component == (Object)null || val == null || !component.IsOwner())) { return; } if (scalars.HasHealthOverride) { mineRock.m_health = scalars.Health; } if (scalars.HasMinToolTierOverride) { mineRock.m_minToolTier = scalars.MinToolTier; } if (!((Object)(object)component == (Object)null || val == null || !component.IsOwner() || flag)) { if (scalars.HasHealthOverride) { SetMineRockAreaHealthAbsolute(mineRock, GetScaledMineHealth(Mathf.Max(scalars.Health, 0.01f))); } val.Set(MineRockLazyScalarSignatureKey, signature, false); } } private static void ApplyLazyMineRock5Scalars(MineRock5 mineRock5, CompiledDamageableScalarDefinition scalars, int signature) { ZNetView component = ((Component)mineRock5).GetComponent(); ZDO val = ((component != null) ? component.GetZDO() : null); bool flag = ((val != null) ? val.GetInt(MineRock5LazyScalarSignatureKey, int.MinValue) : int.MinValue) == signature; bool num = !scalars.HasHealthOverride || Mathf.Approximately(mineRock5.m_health, scalars.Health); bool flag2 = !scalars.HasMinToolTierOverride || mineRock5.m_minToolTier == scalars.MinToolTier; if (num && flag2 && (flag || (Object)(object)component == (Object)null || val == null || !component.IsOwner())) { return; } if (scalars.HasHealthOverride) { mineRock5.m_health = scalars.Health; } if (scalars.HasMinToolTierOverride) { mineRock5.m_minToolTier = scalars.MinToolTier; } if (!((Object)(object)component == (Object)null || val == null || !component.IsOwner() || flag)) { if (scalars.HasHealthOverride) { SetMineRock5AreaHealthAbsolute(mineRock5, GetScaledMineHealth(Mathf.Max(scalars.Health, 0.01f))); } val.Set(MineRock5LazyScalarSignatureKey, signature, false); } } private static void ApplyLazyDestructibleScalars(Destructible destructible, CompiledDestructibleComponentDefinition definition, int signature) { ZNetView component = ((Component)destructible).GetComponent(); ZDO val = ((component != null) ? component.GetZDO() : null); bool flag = ((val != null) ? val.GetInt(DestructibleLazyScalarSignatureKey, int.MinValue) : int.MinValue) == signature; bool num = !definition.HasHealthOverride || Mathf.Approximately(destructible.m_health, definition.Health); bool flag2 = !definition.HasMinToolTierOverride || destructible.m_minToolTier == definition.MinToolTier; if (num && flag2 && (flag || (Object)(object)component == (Object)null || val == null || !component.IsOwner())) { return; } if (definition.HasHealthOverride) { destructible.m_health = definition.Health; } if (definition.HasMinToolTierOverride) { destructible.m_minToolTier = definition.MinToolTier; } if (!((Object)(object)component == (Object)null || val == null || !component.IsOwner() || flag)) { if (definition.HasHealthOverride) { val.Set(ZDOVars.s_health, GetScaledMineHealth(Mathf.Max(definition.Health, 0.01f))); } val.Set(DestructibleLazyScalarSignatureKey, signature, false); } } private static bool TryGetCachedEventDropTable(GameObject gameObject, Func payloadSelector, Func snapshotSelector, LiveObjectComponentKind componentKind, out DropTable? overrideTable) { overrideTable = null; if (!TryGetConditionalContext(gameObject, out string prefabName, out PrefabSnapshot snapshot, out List entries)) { return false; } EnsureRuntimeDropConfigurationState(); if (!DropRuntimeState.Configuration.PlansByPrefab.TryGetValue(prefabName, out CompiledObjectPrefabPlan value) || value.Rules.Count == 0) { return false; } CachedEventDropTableState orCreateEventDropTableState = LazyRuntimeState.GetOrCreateEventDropTableState(gameObject, componentKind); DropTable snapshotTable = snapshotSelector(snapshot); if (orCreateEventDropTableState.Epoch != _reconcileQueueEpoch || !string.Equals(orCreateEventDropTableState.PrefabName, prefabName, StringComparison.Ordinal)) { orCreateEventDropTableState.Reset(_reconcileQueueEpoch, prefabName, snapshotTable); } if (!orCreateEventDropTableState.IsInitialized) { InitializeCachedEventDropTableState(orCreateEventDropTableState, value.Rules, payloadSelector); } if (!orCreateEventDropTableState.HasCustomBlock) { return false; } int num = ComputeCachedEventDropTableRuntimeSignature(gameObject, orCreateEventDropTableState); if (orCreateEventDropTableState.HasCachedResolution && orCreateEventDropTableState.LastRuntimeSignature == num) { overrideTable = orCreateEventDropTableState.LastResolvedDropTable; return orCreateEventDropTableState.LastHasOverride; } GroupConditionalApplyPlan plan = null; TryGetGroupConditionalApplyPlan(gameObject, snapshot, entries, out plan); bool flag = TryBuildEffectiveDropTable(gameObject, prefabName, orCreateEventDropTableState.RelevantRules, payloadSelector, orCreateEventDropTableState.SnapshotTable, componentKind, allowConditionalMatches: true, plan, out overrideTable); orCreateEventDropTableState.LastRuntimeSignature = num; orCreateEventDropTableState.HasCachedResolution = true; orCreateEventDropTableState.LastHasOverride = flag; orCreateEventDropTableState.LastResolvedDropTable = overrideTable; return flag; } private static void InitializeCachedEventDropTableState(CachedEventDropTableState cacheState, IEnumerable compiledRules, Func payloadSelector) { List list = new List(); HashSet destination = null; HashSet destination2 = null; foreach (CompiledObjectDropRule compiledRule in compiledRules) { if (payloadSelector(compiledRule) != null) { cacheState.HasCustomBlock = true; list.Add(compiledRule); ConditionsDefinition conditions = compiledRule.Entry.Conditions; if (DropConditionEvaluator.HasDynamicConditions(conditions)) { cacheState.UsesTimeOfDay |= conditions?.TimeOfDay != null; cacheState.UsesRequiredEnvironments |= HasConfiguredConditionValues(conditions?.RequiredEnvironments); cacheState.UsesInsidePlayerBase |= conditions?.InsidePlayerBase.HasValue ?? false; AddNormalizedConditionValues(conditions?.RequiredGlobalKeys, ref destination); AddNormalizedConditionValues(conditions?.ForbiddenGlobalKeys, ref destination2); } } } cacheState.RelevantRules = list.ToArray(); cacheState.RequiredGlobalKeys = destination?.ToArray() ?? Array.Empty(); cacheState.ForbiddenGlobalKeys = destination2?.ToArray() ?? Array.Empty(); cacheState.IsInitialized = true; } private static bool HasConfiguredConditionValues(IEnumerable? values) { if (values == null) { return false; } foreach (string value in values) { if (!string.IsNullOrWhiteSpace(value)) { return true; } } return false; } private static void AddNormalizedConditionValues(IEnumerable? values, ref HashSet? destination) { if (values == null) { return; } foreach (string value in values) { string text = (value ?? "").Trim(); if (text.Length != 0) { if (destination == null) { destination = new HashSet(StringComparer.Ordinal); } destination.Add(text); } } } private static int ComputeCachedEventDropTableRuntimeSignature(GameObject gameObject, CachedEventDropTableState cacheState) { if (!cacheState.UsesTimeOfDay && !cacheState.UsesRequiredEnvironments && !cacheState.UsesInsidePlayerBase && cacheState.RequiredGlobalKeys.Length == 0 && cacheState.ForbiddenGlobalKeys.Length == 0) { return 0; } EventDropRuntimeContextSnapshot orCreateEventDropRuntimeContextSnapshot = LazyRuntimeState.GetOrCreateEventDropRuntimeContextSnapshot(); int num = 17; if (cacheState.UsesTimeOfDay) { num = CombineCachedEventDropRuntimeSignature(num, orCreateEventDropRuntimeContextSnapshot.TimeOfDayPhaseMarker); } if (cacheState.UsesRequiredEnvironments) { num = CombineCachedEventDropRuntimeSignature(num, orCreateEventDropRuntimeContextSnapshot.EnvironmentName); } if (cacheState.UsesInsidePlayerBase) { num = CombineCachedEventDropRuntimeSignature(num, GetCachedEventInsidePlayerBaseState(gameObject, cacheState)); } string[] requiredGlobalKeys = cacheState.RequiredGlobalKeys; foreach (string text in requiredGlobalKeys) { num = CombineCachedEventDropRuntimeSignature(num, text); num = CombineCachedEventDropRuntimeSignature(num, GetCachedEventGlobalKeyState(orCreateEventDropRuntimeContextSnapshot, text)); } requiredGlobalKeys = cacheState.ForbiddenGlobalKeys; foreach (string text2 in requiredGlobalKeys) { num = CombineCachedEventDropRuntimeSignature(num, text2); num = CombineCachedEventDropRuntimeSignature(num, GetCachedEventGlobalKeyState(orCreateEventDropRuntimeContextSnapshot, text2)); } return num; } private static bool GetCachedEventInsidePlayerBaseState(GameObject gameObject, CachedEventDropTableState cacheState) { //IL_001c: Unknown result type (might be due to invalid IL or missing references) float realtimeSinceStartup = Time.realtimeSinceStartup; if (realtimeSinceStartup - cacheState.LastInsidePlayerBaseSampleTime >= 0.5f) { cacheState.IsInsidePlayerBase = (Object)(object)EffectArea.IsPointInsideArea(gameObject.transform.position, (Type)4, 0f) != (Object)null; cacheState.LastInsidePlayerBaseSampleTime = realtimeSinceStartup; } return cacheState.IsInsidePlayerBase; } private static bool GetCachedEventGlobalKeyState(EventDropRuntimeContextSnapshot runtimeContext, string key) { if (runtimeContext.GlobalKeyStates.TryGetValue(key, out var value)) { return value; } value = (Object)(object)ZoneSystem.instance != (Object)null && ZoneSystem.instance.GetGlobalKey(key); runtimeContext.GlobalKeyStates[key] = value; return value; } private static int CombineCachedEventDropRuntimeSignature(int current, bool value) { return current * 31 + (value ? 1 : 0); } private static int CombineCachedEventDropRuntimeSignature(int current, int value) { return current * 31 + value; } private static int CombineCachedEventDropRuntimeSignature(int current, string value) { return current * 31 + (value?.GetHashCode() ?? 0); } private static bool PickableItemRandomItemsEqual(IReadOnlyList currentItems, IReadOnlyList desiredItems) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_002a: 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_003d: 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_004b: Unknown result type (might be due to invalid IL or missing references) if (currentItems.Count != desiredItems.Count) { return false; } for (int i = 0; i < currentItems.Count; i++) { RandomItem val = currentItems[i]; RandomItem val2 = desiredItems[i]; if ((Object)(object)val.m_itemPrefab != (Object)(object)val2.m_itemPrefab || val.m_stackMin != val2.m_stackMin || val.m_stackMax != val2.m_stackMax) { return false; } } return true; } private static bool PickableItemRandomWeightsEqual(PickableItem pickableItem, IReadOnlyList desiredWeights) { if (!LazyRuntimeState.TryGetRandomState(pickableItem, out WeightedRandomPickableItemState state)) { return desiredWeights.Count == 0; } if (state.Weights.Length != desiredWeights.Count) { return false; } for (int i = 0; i < state.Weights.Length; i++) { if (!Mathf.Approximately(state.Weights[i], desiredWeights[i])) { return false; } } return true; } private static BuiltRandomPickableItems BuildRandomItems(List definitions, string context) { //IL_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_0135: Unknown result type (might be due to invalid IL or missing references) List list = new List(); List list2 = new List(); foreach (RandomPickableItemDefinition definition in definitions) { if (string.IsNullOrWhiteSpace(definition.Item)) { WarnInvalidEntry("Entry '" + context + "' contains a randomDrops item without an item name."); continue; } float num = definition.Weight ?? 1f; if (float.IsNaN(num) || float.IsInfinity(num) || num <= 0f) { WarnInvalidEntry($"Entry '{context}' contains a randomDrops item for '{definition.Item}' with invalid weight '{num}'. Weight must be greater than 0."); continue; } GameObject val = ResolveItemPrefab(definition.Item, context); if (!((Object)(object)val == (Object)null)) { list.Add(new RandomItem { m_itemPrefab = val.GetComponent(), m_stackMin = Math.Max(1, definition.StackMin ?? 1), m_stackMax = Math.Max(Math.Max(1, definition.StackMin ?? 1), definition.StackMax ?? definition.StackMin ?? 1) }); list2.Add(num); } } return new BuiltRandomPickableItems { Items = list.ToArray(), Weights = list2.ToArray() }; } private static void RestorePickableItem(PickableItem pickableItem, PickableItemSnapshot snapshot, bool updateRuntimeState) { ClearPickableItemRandomWeights(pickableItem); pickableItem.m_itemPrefab = (Object.op_Implicit((Object)(object)snapshot.ItemPrefab) ? snapshot.ItemPrefab.GetComponent() : null); pickableItem.m_stack = snapshot.Stack; pickableItem.m_randomItemPrefabs = ((IEnumerable)snapshot.RandomItems).Select((Func)((PickableItemRandomSnapshot item) => new RandomItem { m_itemPrefab = (Object.op_Implicit((Object)(object)item.ItemPrefab) ? item.ItemPrefab.GetComponent() : null), m_stackMin = item.StackMin, m_stackMax = item.StackMax })).ToArray(); if (updateRuntimeState) { UpdatePickableItemRuntimeState(pickableItem, pickableItem.m_randomItemPrefabs.Length != 0 && (Object)(object)pickableItem.m_itemPrefab == (Object)null, forceRandomRefresh: false); } } private static void SetPickableItemRandomWeights(PickableItem pickableItem, IReadOnlyList weights) { LazyRuntimeState.SetRandomWeights(pickableItem, weights.ToArray()); } private static void ClearPickableItemRandomWeights(PickableItem pickableItem) { LazyRuntimeState.ClearRandomState(pickableItem); } private static RandomItem SelectRandomPickableItem(PickableItem pickableItem) { //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) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_00f4: Unknown result type (might be due to invalid IL or missing references) //IL_00d9: Unknown result type (might be due to invalid IL or missing references) RandomItem[] randomItemPrefabs = pickableItem.m_randomItemPrefabs; if (randomItemPrefabs.Length == 0) { return default(RandomItem); } if (!LazyRuntimeState.TryGetRandomState(pickableItem, out WeightedRandomPickableItemState state) || state.Weights.Length != randomItemPrefabs.Length) { return randomItemPrefabs[Random.Range(0, randomItemPrefabs.Length)]; } float num = 0f; for (int i = 0; i < state.Weights.Length; i++) { num += Math.Max(0f, state.Weights[i]); } if (num <= 0f) { return randomItemPrefabs[Random.Range(0, randomItemPrefabs.Length)]; } float num2 = Random.Range(0f, num); float num3 = 0f; int num4 = 0; for (int j = 0; j < state.Weights.Length; j++) { float num5 = Math.Max(0f, state.Weights[j]); if (!(num5 <= 0f)) { num4 = j; num3 += num5; if (num2 <= num3) { return randomItemPrefabs[j]; } } } return randomItemPrefabs[num4]; } private static void UpdatePickableItemRuntimeState(PickableItem pickableItem, bool useRandomItems, bool forceRandomRefresh) { //IL_0128: 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_0130: Unknown result type (might be due to invalid IL or missing references) //IL_014e: Unknown result type (might be due to invalid IL or missing references) ZNetView component = ((Component)pickableItem).GetComponent(); ZDO val = ((component != null) ? component.GetZDO() : null); if (useRandomItems && pickableItem.m_randomItemPrefabs.Length != 0) { bool flag = val != null && (Object)(object)component != (Object)null; bool flag2 = (Object)(object)component != (Object)null && component.IsOwner(); bool flag3 = val != null && val.GetInt(ZDOVars.s_itemPrefab, 0) != 0; if (forceRandomRefresh && val != null && flag2) { val.Set(ZDOVars.s_itemPrefab, 0, false); val.Set(ZDOVars.s_itemStack, 0, false); flag3 = false; } if (flag3) { int num = val.GetInt(ZDOVars.s_itemPrefab, 0); ObjectDB instance = ObjectDB.instance; GameObject val2 = ((instance != null) ? instance.GetItemPrefab(num) : null); ItemDrop val3 = (Object.op_Implicit((Object)(object)val2) ? val2.GetComponent() : null); if ((Object)(object)val3 != (Object)null) { pickableItem.m_itemPrefab = val3; pickableItem.m_stack = Math.Max(1, val.GetInt(ZDOVars.s_itemStack, Math.Max(1, pickableItem.m_stack))); } else { flag3 = false; } } if (!flag3 && flag && !flag2) { pickableItem.m_itemPrefab = null; pickableItem.m_stack = 1; RefreshPickableItemVisual(pickableItem); return; } if (!flag3) { RandomItem val4 = SelectRandomPickableItem(pickableItem); pickableItem.m_itemPrefab = val4.m_itemPrefab; if ((Object)(object)pickableItem.m_itemPrefab != (Object)null) { pickableItem.m_stack = ScalePickableItemStack(val4); ObjectDB instance2 = ObjectDB.instance; if (val != null && (Object)(object)component != (Object)null && component.IsOwner() && (Object)(object)instance2 != (Object)null) { int prefabHash = instance2.GetPrefabHash(((Component)pickableItem.m_itemPrefab).gameObject); val.Set(ZDOVars.s_itemPrefab, prefabHash, false); val.Set(ZDOVars.s_itemStack, Math.Max(1, pickableItem.m_stack), false); } } } } else if (val != null && (Object)(object)component != (Object)null && component.IsOwner()) { if ((Object)(object)pickableItem.m_itemPrefab != (Object)null) { ObjectDB instance3 = ObjectDB.instance; if ((Object)(object)instance3 == (Object)null) { RefreshPickableItemVisual(pickableItem); return; } int prefabHash2 = instance3.GetPrefabHash(((Component)pickableItem.m_itemPrefab).gameObject); val.Set(ZDOVars.s_itemPrefab, prefabHash2, false); val.Set(ZDOVars.s_itemStack, Math.Max(1, pickableItem.m_stack), false); } else { val.Set(ZDOVars.s_itemPrefab, 0, false); val.Set(ZDOVars.s_itemStack, 0, false); } } RefreshPickableItemVisual(pickableItem); } private static int ScalePickableItemStack(RandomItem randomItem) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) int num = Math.Max(1, randomItem.m_stackMin); int num2 = Math.Max(num, randomItem.m_stackMax); if ((Object)(object)randomItem.m_itemPrefab == (Object)null) { return num; } if ((Object)(object)Game.instance != (Object)null) { return Game.instance.ScaleDrops(randomItem.m_itemPrefab.m_itemData, num, num2 + 1); } return Random.Range(num, num2 + 1); } private static void RefreshPickableItemVisual(PickableItem pickableItem) { if (!(PickableItemSetupItemMethod == null)) { PickableItemSetupItemMethod.Invoke(pickableItem, new object[1] { false }); PickableItemSetupItemMethod.Invoke(pickableItem, new object[1] { true }); } } internal static void TrackObjectInstance(GameObject? gameObject) { lock (Sync) { TryTrackLiveObjectInstanceLocked(gameObject, out string _); } } internal static void UntrackObjectInstance(GameObject? gameObject) { lock (Sync) { UntrackLiveObjectInstanceLocked(gameObject); } } private static IEnumerable GetRegisteredLiveObjects() { EnsureLiveObjectRegistrySessionLocked(); HashSet hashSet = SnapshotPrefabsRequiringLiveTracking(); BootstrapLiveObjectRegistryIfNeededLocked(hashSet); CleanupRegisteredLiveObjects(); return LiveRegistryState.EnumerateRegisteredLiveObjects(hashSet); } private static IEnumerable GetRegisteredLiveObjects(HashSet dirtyPrefabs) { EnsureLiveObjectRegistrySessionLocked(); HashSet hashSet = FilterPrefabsRequiringLiveTracking(dirtyPrefabs); BootstrapLiveObjectRegistryIfNeededLocked(hashSet); CleanupRegisteredLiveObjects(); return LiveRegistryState.EnumerateRegisteredLiveObjects(hashSet); } private static void EnsureLiveObjectRegistrySessionLocked() { int currentSceneInstanceId = (((Object)(object)ZNetScene.instance != (Object)null) ? ((Object)ZNetScene.instance).GetInstanceID() : 0); if (!LiveRegistryState.HasSceneSession(currentSceneInstanceId)) { LiveRegistryState.BeginSceneSession(currentSceneInstanceId); ClearLiveObjectConditionCaches(); } } private static void BootstrapLiveObjectRegistryIfNeededLocked(HashSet targetPrefabs) { if (targetPrefabs == null || targetPrefabs.Count == 0) { return; } HashSet missingPrefabs = LiveRegistryState.CollectUnbootstrappedTrackedPrefabs(targetPrefabs); if (missingPrefabs.Count == 0) { return; } VisitActiveLoadedSceneGameObjects(delegate(GameObject gameObject) { if (HasRelevantLiveObjectComponents(gameObject)) { string prefabName = GetPrefabName(gameObject); if (prefabName.Length != 0 && missingPrefabs.Contains(prefabName)) { RegisterLiveObject(gameObject, prefabName); } } }); LiveRegistryState.MarkTrackedPrefabsBootstrapped(missingPrefabs); } private static bool TryTrackLiveObjectInstanceLocked(GameObject? gameObject, out string prefabName) { EnsureLiveObjectRegistrySessionLocked(); prefabName = ""; if ((Object)(object)gameObject == (Object)null) { return false; } if (LiveRegistryState.TryGetTrackedPrefab(gameObject, out string prefabName2) && !string.IsNullOrWhiteSpace(prefabName2)) { prefabName = prefabName2; return true; } prefabName = GetPrefabName(gameObject); if (prefabName.Length == 0) { return false; } if (!RequiresLiveTrackingForPrefab(prefabName)) { return true; } RegisterLiveObject(gameObject, prefabName); return true; } private static void UntrackLiveObjectInstanceLocked(GameObject? gameObject) { if (!((Object)(object)gameObject == (Object)null)) { int instanceID = ((Object)gameObject).GetInstanceID(); RemovePendingObjectReconcileState(instanceID); RemovePendingObjectConditionPlanState(instanceID); if (LiveRegistryState.TryGetTrackedPrefab(gameObject, out string prefabName) && !string.IsNullOrWhiteSpace(prefabName)) { UnregisterLiveObject(gameObject, prefabName); } } } private static void RegisterLiveObject(GameObject gameObject, string prefabName) { if (!((Object)(object)gameObject == (Object)null) && prefabName.Length != 0 && RequiresLiveTrackingForPrefab(prefabName)) { LiveRegistryState.Register(gameObject, prefabName); } } private static void CleanupRegisteredLiveObjects() { List> list = new List>(); LiveRegistryState.CollectDeadObjects(list); if (list.Count == 0) { return; } foreach (var (gameObject, prefabName) in list) { UnregisterLiveObject(gameObject, prefabName); } } private static void UnregisterLiveObject(GameObject gameObject, string prefabName) { LiveRegistryState.Unregister(gameObject, prefabName); } private static bool HasRelevantLiveObjectComponents(GameObject gameObject) { if ((Object)(object)gameObject == (Object)null) { return false; } DropOnDestroyed val = default(DropOnDestroyed); if (gameObject.TryGetComponent(ref val)) { return true; } MineRock val2 = default(MineRock); if (gameObject.TryGetComponent(ref val2)) { return true; } MineRock5 val3 = default(MineRock5); if (gameObject.TryGetComponent(ref val3)) { return true; } TreeBase val4 = default(TreeBase); if (gameObject.TryGetComponent(ref val4)) { return true; } TreeLog val5 = default(TreeLog); if (gameObject.TryGetComponent(ref val5)) { return true; } Container val6 = default(Container); if (gameObject.TryGetComponent(ref val6)) { return true; } Pickable val7 = default(Pickable); if (gameObject.TryGetComponent(ref val7)) { return true; } PickableItem val8 = default(PickableItem); if (gameObject.TryGetComponent(ref val8)) { return true; } Fish val9 = default(Fish); if (gameObject.TryGetComponent(ref val9)) { return true; } Destructible val10 = default(Destructible); return gameObject.TryGetComponent(ref val10); } private static void VisitActiveLoadedSceneGameObjects(Action visitor) { SceneTraversalSupport.VisitActiveLoadedSceneGameObjects(visitor); } internal static void ReconcilePieceInstance(Piece piece) { ReconcileObjectInstance(((Component)piece).gameObject); } internal static void ReconcileObjectInstance(GameObject gameObject) { lock (Sync) { ReconcileObjectInstanceCore(gameObject); } } private static void ReconcileObjectInstanceCore(GameObject? gameObject) { if (!TryTrackLiveObjectInstanceLocked(gameObject, out string prefabName) || !ShouldReconcileLocally(gameObject)) { return; } List value; bool flag = RuntimeState.ActiveEntriesByPrefab.TryGetValue(prefabName, out value) && value.Count > 0; if (IsGameDataReady() && !DropNSpawnPlugin.IsGameDataRefreshDeferred(DropNSpawnPlugin.ReloadDomain.Object) && SnapshotState.SnapshotsByPrefab.TryGetValue(prefabName, out PrefabSnapshot value2) && RequiresLiveReconcileForPrefab(prefabName)) { RestoreConfiguredComponents(gameObject, value2, CreateRestoreMask(value2), updateRuntimeState: true); if (PluginSettingsFacade.IsObjectDomainEnabled() && flag) { ReconcileConfiguredInstance(gameObject, value2, value); } } } private static void ReconcileConfiguredInstance(GameObject gameObject, PrefabSnapshot snapshot, IEnumerable entries) { List list = (entries as List) ?? entries.ToList(); if (!ShouldApplyToInstance(gameObject)) { return; } TryGetGroupConditionalApplyPlan(gameObject, snapshot, list, out GroupConditionalApplyPlan plan); if (plan != null) { foreach (PrefabConfigurationEntry matchingEntry in plan.MatchingEntries) { ApplyConfiguredComponents(gameObject, snapshot, matchingEntry, updateRuntimeState: true, allowConditionalMatches: true); } } foreach (PrefabConfigurationEntry item in list) { if (plan == null || !plan.EligibleEntries.Contains(item)) { ApplyConfiguredComponents(gameObject, snapshot, item, updateRuntimeState: true, allowConditionalMatches: true); } } ApplyEffectiveDropTableOverrides(gameObject, snapshot, list, allowConditionalMatches: true, plan, includeEventOnlyKinds: false); } private static void RefreshConfiguredPrefabProfile(string prefabName) { PrefabProfileCatalogState.RefreshConfiguredPrefabProfile(RuntimeState.ActiveEntriesByPrefab, prefabName); } private static void RefreshConfiguredPrefabProfiles(Dictionary> activeEntriesByPrefab, Dictionary configuredComponentKindsByPrefab, Dictionary reconcileComponentKindsByPrefab) { configuredComponentKindsByPrefab.Clear(); reconcileComponentKindsByPrefab.Clear(); foreach (string key in activeEntriesByPrefab.Keys) { RefreshConfiguredPrefabProfile(activeEntriesByPrefab, configuredComponentKindsByPrefab, key); RefreshReconcilePrefabProfile(activeEntriesByPrefab, reconcileComponentKindsByPrefab, key); } } private static void RefreshConfiguredPrefabProfile(Dictionary> activeEntriesByPrefab, Dictionary configuredComponentKindsByPrefab, string prefabName) { if (!activeEntriesByPrefab.TryGetValue(prefabName, out List value) || value.Count == 0) { configuredComponentKindsByPrefab.Remove(prefabName); return; } LiveObjectComponentKind liveObjectComponentKind = LiveObjectComponentKind.None; foreach (PrefabConfigurationEntry item in value) { liveObjectComponentKind |= GetConfiguredComponentKinds(item); } if (liveObjectComponentKind == LiveObjectComponentKind.None) { configuredComponentKindsByPrefab.Remove(prefabName); } else { configuredComponentKindsByPrefab[prefabName] = liveObjectComponentKind; } } private static void RefreshReconcilePrefabProfile(Dictionary> activeEntriesByPrefab, Dictionary reconcileComponentKindsByPrefab, string prefabName) { if (!activeEntriesByPrefab.TryGetValue(prefabName, out List value) || value.Count == 0) { reconcileComponentKindsByPrefab.Remove(prefabName); return; } LiveObjectComponentKind liveObjectComponentKind = LiveObjectComponentKind.None; foreach (PrefabConfigurationEntry item in value) { liveObjectComponentKind |= GetReconcileComponentKinds(item); } if (liveObjectComponentKind == LiveObjectComponentKind.None) { reconcileComponentKindsByPrefab.Remove(prefabName); } else { reconcileComponentKindsByPrefab[prefabName] = liveObjectComponentKind; } } private static LiveObjectComponentKind GetConfiguredComponentKinds(PrefabConfigurationEntry entry) { LiveObjectComponentKind liveObjectComponentKind = LiveObjectComponentKind.None; if (entry.DropOnDestroyed != null) { liveObjectComponentKind |= LiveObjectComponentKind.DropOnDestroyed; } if (entry.MineRock != null) { liveObjectComponentKind |= LiveObjectComponentKind.MineRock; } if (entry.MineRock5 != null) { liveObjectComponentKind |= LiveObjectComponentKind.MineRock5; } if (entry.TreeBase != null) { liveObjectComponentKind |= LiveObjectComponentKind.TreeBase; } if (entry.TreeLog != null) { liveObjectComponentKind |= LiveObjectComponentKind.TreeLog; } if (entry.Container != null) { liveObjectComponentKind |= LiveObjectComponentKind.Container; } if (entry.Pickable != null) { liveObjectComponentKind |= LiveObjectComponentKind.Pickable; } if (entry.PickableItem != null) { liveObjectComponentKind |= LiveObjectComponentKind.PickableItem; } if (entry.Fish != null) { liveObjectComponentKind |= LiveObjectComponentKind.Fish; } if (RequiresLiveReconcile(entry, entry.Destructible)) { liveObjectComponentKind |= LiveObjectComponentKind.Destructible; } return liveObjectComponentKind; } private static LiveObjectComponentKind GetReconcileComponentKinds(PrefabConfigurationEntry entry) { LiveObjectComponentKind liveObjectComponentKind = LiveObjectComponentKind.None; if (RequiresLiveReconcile(entry.DropOnDestroyed, LiveObjectComponentKind.DropOnDestroyed)) { liveObjectComponentKind |= LiveObjectComponentKind.DropOnDestroyed; } if (RequiresLiveReconcile(entry.MineRock, LiveObjectComponentKind.MineRock) && !CanUseLazyDamageableScalarFastPath(entry, LiveObjectComponentKind.MineRock)) { liveObjectComponentKind |= LiveObjectComponentKind.MineRock; } if (RequiresLiveReconcile(entry.MineRock5, LiveObjectComponentKind.MineRock5) && !CanUseLazyDamageableScalarFastPath(entry, LiveObjectComponentKind.MineRock5)) { liveObjectComponentKind |= LiveObjectComponentKind.MineRock5; } if (RequiresLiveReconcile(entry.TreeBase, LiveObjectComponentKind.TreeBase) && !CanUseLazyDamageableScalarFastPath(entry, LiveObjectComponentKind.TreeBase)) { liveObjectComponentKind |= LiveObjectComponentKind.TreeBase; } if (RequiresLiveReconcile(entry.TreeLog, LiveObjectComponentKind.TreeLog) && !CanUseLazyDamageableScalarFastPath(entry, LiveObjectComponentKind.TreeLog)) { liveObjectComponentKind |= LiveObjectComponentKind.TreeLog; } if (entry.Pickable != null) { liveObjectComponentKind |= LiveObjectComponentKind.Pickable; } if (entry.PickableItem != null) { liveObjectComponentKind |= LiveObjectComponentKind.PickableItem; } if (entry.Fish != null) { liveObjectComponentKind |= LiveObjectComponentKind.Fish; } if (RequiresLiveReconcile(entry, entry.Destructible)) { liveObjectComponentKind |= LiveObjectComponentKind.Destructible; } return liveObjectComponentKind; } private static bool RequiresLiveReconcileForPrefab(string prefabName) { return PrefabProfileCatalogState.RequiresLiveReconcile(prefabName); } private static bool RequiresLiveTrackingForPrefab(string prefabName) { return PrefabProfileCatalogState.RequiresLiveObjectTracking(prefabName); } private static bool RequiresLiveReconcileForPrefab(string prefabName, LiveObjectComponentKind componentKind) { return PrefabProfileCatalogState.RequiresLiveReconcile(prefabName, componentKind); } private static HashSet FilterPrefabsRequiringLiveReconcile(IEnumerable? prefabNames) { return PrefabProfileCatalogState.FilterPrefabsRequiringLiveReconcile(prefabNames); } private static HashSet FilterPrefabsRequiringLiveTracking(IEnumerable? prefabNames) { return PrefabProfileCatalogState.FilterPrefabsRequiringLiveTracking(prefabNames); } private static HashSet SnapshotPrefabsRequiringLiveTracking() { return PrefabProfileCatalogState.SnapshotPrefabsRequiringLiveTracking(); } private static void ReplaceComponentKinds(Dictionary target, Dictionary source) { target.Clear(); foreach (var (key, liveObjectComponentKind2) in source) { if (liveObjectComponentKind2 != LiveObjectComponentKind.None) { target[key] = liveObjectComponentKind2; } } } internal static void QueueObjectInstanceReconcile(GameObject? gameObject, LiveObjectComponentKind sourceKind) { lock (Sync) { if (!((Object)(object)gameObject == (Object)null) && ShouldReconcileLocally(gameObject)) { string prefabName = GetPrefabName(gameObject); if (prefabName.Length != 0 && ShouldQueueAwakeReconcileForPrefab(prefabName, sourceKind) && TryTrackLiveObjectInstanceLocked(gameObject, out prefabName)) { QueueTrackedObjectInstanceReconcileLocked(gameObject, prefabName); } } } } internal static bool HasPendingReconcileWork() { lock (Sync) { return HasPendingReconcileWorkLocked(); } } private static bool HasPendingReconcileWorkLocked() { return ReconcileQueueState.HasPendingWork(); } internal static bool ProcessQueuedReconcileStep(double deadline) { lock (Sync) { return TryProcessQueuedReconcileWorkLocked(deadline); } } private static bool TryProcessQueuedReconcileWorkLocked(double deadline) { return TryProcessNextQueuedReconcileItemLocked(deadline, highPriorityOnly: false); } private static void DrainQueuedHighPriorityReconcilesLocked() { while (TryProcessNextQueuedReconcileItemLocked(double.MaxValue, highPriorityOnly: true)) { } } private static bool TryProcessNextQueuedReconcileItemLocked(double deadline, bool highPriorityOnly) { while (ReconcileQueueState.HasPendingGroups(highPriorityOnly)) { if (!highPriorityOnly && Time.realtimeSinceStartupAsDouble >= deadline) { return false; } if (!IsGameDataReady() || DropNSpawnPlugin.IsGameDataRefreshDeferred(DropNSpawnPlugin.ReloadDomain.Object)) { return false; } if (!TryDequeuePendingObjectReconcileGroup(out var queuedGroup, highPriorityOnly) || !ReconcileQueueState.TryGetGroupState(queuedGroup.GroupKey, out PendingObjectReconcileGroupState groupState)) { continue; } groupState.IsQueued = false; if (queuedGroup.Epoch != _reconcileQueueEpoch) { continue; } while (groupState.Items.Count > 0) { if (!groupState.Items.TryDequeue(out var item)) { continue; } groupState.InstanceIds.Remove(item.InstanceId); if ((Object)(object)item.GameObject == (Object)null) { ReconcileQueueState.RemovePendingState(item.InstanceId); } else if (ReconcileQueueState.TryTakePendingInstance(item.InstanceId)) { ReconcileObjectInstanceCore(item.GameObject); if (groupState.Items.Count > 0) { EnqueueObjectReconcileGroup(queuedGroup.GroupKey, groupState); } else { ReconcileQueueState.RemoveGroup(queuedGroup.GroupKey); } return true; } } ReconcileQueueState.RemoveGroup(queuedGroup.GroupKey); } return false; } private static bool ShouldQueueAwakeReconcileForPrefab(string prefabName, LiveObjectComponentKind sourceKind) { if (prefabName.Length == 0) { return false; } if (sourceKind == LiveObjectComponentKind.Piece) { return RequiresLiveReconcileForPrefab(prefabName); } if (PrefabProfileCatalogState.TryGetReconcileKinds(prefabName, out var reconcileKinds)) { return (reconcileKinds & sourceKind) != 0; } return false; } private static string BuildObjectReconcileGroupKey(GameObject gameObject, string prefabName) { //IL_0053: 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_0063: 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) Transform root = gameObject.transform.root; if ((Object)(object)root != (Object)null && (Object)(object)((Component)root).gameObject != (Object)null && (Object)(object)((Component)root).gameObject != (Object)(object)gameObject) { return $"root:{((Object)((Component)root).gameObject).GetInstanceID()}:{prefabName}"; } Vector2i zone = ZoneSystem.GetZone(gameObject.transform.position); return $"zone:{zone.x}:{zone.y}:{prefabName}"; } private static bool IsHighPriorityPrefab(string prefabName) { if (!PrefabProfileCatalogState.TryGetReconcileKinds(prefabName, out var reconcileKinds)) { return false; } LiveObjectComponentKind liveObjectComponentKind = LiveObjectComponentKind.Pickable | LiveObjectComponentKind.PickableItem | LiveObjectComponentKind.Fish; return (reconcileKinds & liveObjectComponentKind) != 0; } private static void PromoteQueuedReconcileGroupsLocked(LiveObjectComponentKind componentKinds) { foreach (var (groupKey, pendingObjectReconcileGroupState2) in ReconcileQueueState.EnumerateGroups()) { if (pendingObjectReconcileGroupState2.IsQueued && !pendingObjectReconcileGroupState2.HighPriority && (pendingObjectReconcileGroupState2.ComponentKinds & componentKinds) != LiveObjectComponentKind.None) { pendingObjectReconcileGroupState2.HighPriority = true; ReconcileQueueState.EnqueueGroup(new PendingObjectReconcileGroup(groupKey, _reconcileQueueEpoch), highPriority: true); } } } private static void EnqueueObjectReconcileGroup(string groupKey, PendingObjectReconcileGroupState groupState) { if (!groupState.IsQueued) { groupState.IsQueued = true; PendingObjectReconcileGroup queuedGroup = new PendingObjectReconcileGroup(groupKey, _reconcileQueueEpoch); ReconcileQueueState.EnqueueGroup(queuedGroup, groupState.HighPriority); } } private static bool TryDequeuePendingObjectReconcileGroup(out PendingObjectReconcileGroup queuedGroup, bool highPriorityOnly = false) { return ReconcileQueueState.TryDequeueGroup(out queuedGroup, highPriorityOnly); } private static void QueueTrackedObjectInstanceReconcileLocked(GameObject gameObject, string prefabName) { int instanceID = ((Object)gameObject).GetInstanceID(); if (ReconcileQueueState.TryAddPendingInstance(instanceID)) { string groupKey = BuildObjectReconcileGroupKey(gameObject, prefabName); PrefabProfileCatalogState.TryGetReconcileKinds(prefabName, out var reconcileKinds); PendingObjectReconcileGroupState orCreateGroup = ReconcileQueueState.GetOrCreateGroup(groupKey, reconcileKinds, IsHighPriorityPrefab(prefabName)); if (orCreateGroup.InstanceIds.Add(instanceID)) { orCreateGroup.Items.Enqueue(new PendingObjectReconcileItem(gameObject, instanceID)); EnqueueObjectReconcileGroup(groupKey, orCreateGroup); } } } private static void RemovePendingObjectReconcileState(int instanceId) { ReconcileQueueState.RemovePendingState(instanceId); } private static void ClearQueuedReconcileState() { _reconcileQueueEpoch++; ReconcileQueueState.Clear(); ClearLiveObjectConditionCaches(); } private static List> BuildConfigurationTemplate() { Dictionary locationBuckets = BuildLocationReferenceBuckets(); List> list = PrefabOutputSections.BuildSections(SnapshotState.Snapshots.Select(BuildConfigurationEntry), (PrefabConfigurationEntry entry) => entry.Prefab, (PrefabConfigurationEntry entry) => ResolveObjectOwnerName(entry.Prefab, locationBuckets)); foreach (PrefabOwnerSection item in list) { item.Entries.Sort(CompareObjectEntriesForOutput); } return list; } private static List BuildReferenceEntries() { List list = (from entry in BuildConfigurationTemplate().SelectMany((PrefabOwnerSection section) => section.Entries) select new PrefabReferenceEntry { Prefab = entry.Prefab, DropOnDestroyed = entry.DropOnDestroyed, MineRock = entry.MineRock, MineRock5 = entry.MineRock5, TreeBase = entry.TreeBase, TreeLog = entry.TreeLog, Container = entry.Container, Destructible = entry.Destructible, Pickable = entry.Pickable, PickableItem = entry.PickableItem, Fish = entry.Fish }).ToList(); foreach (PrefabReferenceEntry item in BuildSupplementalLocationReferenceEntries(ReferenceRefreshSupport.ToNormalizedKeySet(list.Select((PrefabReferenceEntry entry) => entry.Prefab)))) { list.Add(item); } return list; } private static string BuildReferenceConfigurationTemplate() { return SerializeReferenceEntries(BuildReferenceEntries()); } private static string SerializeReferenceEntries(IEnumerable entries) { Dictionary locationBuckets = BuildLocationReferenceBuckets(); List> list = PrefabOutputSections.BuildSections(entries, (PrefabReferenceEntry entry) => entry.Prefab, (PrefabReferenceEntry entry) => ResolveObjectOwnerName(entry.Prefab, locationBuckets)); foreach (PrefabOwnerSection item in list) { item.Entries.Sort((PrefabReferenceEntry left, PrefabReferenceEntry right) => StringComparer.OrdinalIgnoreCase.Compare(left.Prefab, right.Prefab)); } return PrefabOutputSections.SerializeReferenceSections(list, Serializer); } internal static bool TryWriteFullScaffoldConfigurationFile(out string path, out string error) { string content; string logMessage; lock (Sync) { path = FullScaffoldConfigurationPath; error = ""; if (!IsGameDataReady() && SnapshotState.Snapshots.Count == 0) { error = "Object game data is not ready yet."; return false; } CaptureSnapshotsIfNeeded(); content = BuildFullScaffoldConfigurationTemplate(); logMessage = "Wrote object full scaffold configuration to " + path + "."; } GeneratedArtifactWriter.WriteTextAlways(path, content, logMessage); return true; } internal static void RefreshReferenceConfigurationFile() { string content; string content2; string sourceSignature; string logMessage; lock (Sync) { if (!IsGameDataReady()) { return; } CaptureSnapshotsIfNeeded(); content = BuildReferenceConfigurationTemplate(); content2 = BuildLocationReferenceConfigurationTemplate(); sourceSignature = ComputeReferenceSourceSignature(); logMessage = "Updated object reference configurations at " + ReferenceConfigurationPath + " and " + LocationReferenceConfigurationPath + "."; } WriteReferenceConfigurationFile(content, logMessage); WriteLocationReferenceConfigurationFile(content2); ReferenceArtifactLifecycle.RecordUpdate("object", ReferenceConfigurationPath, sourceSignature); ReferenceArtifactLifecycle.RecordUpdate("object.locations", LocationReferenceConfigurationPath, sourceSignature); } private static void WriteReferenceConfigurationFile(string content, string logMessage) { GeneratedArtifactWriter.WriteText(ReferenceConfigurationPath, content, logMessage); } private static string BuildLocationReferenceConfigurationTemplate() { return SerializeLocationReferenceEntries(BuildLocationReferenceEntries()); } private static List BuildLocationReferenceEntries() { return (from pair in BuildLocationReferenceBuckets().OrderBy, string>((KeyValuePair pair) => pair.Key, StringComparer.OrdinalIgnoreCase) select new ObjectLocationReferenceEntry { Prefab = pair.Key, Components = pair.Value.Components.ToList(), Locations = pair.Value.Locations.ToList() }).ToList(); } private static string SerializeLocationReferenceEntries(IEnumerable entries) { Dictionary locationBuckets = BuildLocationReferenceBuckets(); List> list = PrefabOutputSections.BuildSections(entries, (ObjectLocationReferenceEntry entry) => entry.Prefab, (ObjectLocationReferenceEntry entry) => ResolveObjectOwnerName(entry.Prefab, locationBuckets)); foreach (PrefabOwnerSection item in list) { item.Entries.Sort((ObjectLocationReferenceEntry left, ObjectLocationReferenceEntry right) => StringComparer.OrdinalIgnoreCase.Compare(left.Prefab, right.Prefab)); } return PrefabOutputSections.SerializeReferenceSections(list, Serializer); } private static string FormatYamlBool(bool value) { if (!value) { return "false"; } return "true"; } private static string FormatYamlFloat(float value) { return value.ToString("0.###", CultureInfo.InvariantCulture); } private static string FormatYamlString(string value) { if (value.Length == 0) { return "''"; } if (!char.IsWhiteSpace(value[0]) && !char.IsWhiteSpace(value[value.Length - 1]) && value.IndexOfAny(new char[17] { ':', '#', '{', '}', '[', ']', ',', '\'', '"', '&', '*', '!', '|', '>', '%', '@', '`' }) < 0 && value[0] != '-' && value[0] != '?' && !string.Equals(value, "null", StringComparison.OrdinalIgnoreCase) && !string.Equals(value, "true", StringComparison.OrdinalIgnoreCase) && !string.Equals(value, "false", StringComparison.OrdinalIgnoreCase)) { return value; } return "'" + value.Replace("'", "''") + "'"; } private static PrefabConfigurationEntry BuildConfigurationEntry(PrefabSnapshot snapshot) { return new PrefabConfigurationEntry { Prefab = ((Object)snapshot.Prefab).name, Enabled = true, Destructible = ConvertDestructible(snapshot), DropOnDestroyed = ((snapshot.DropOnDestroyed != null) ? ConvertDropTable(snapshot.DropOnDestroyed) : null), MineRock = ((snapshot.MineRock != null) ? ConvertDamageableDropTable(snapshot.MineRock, snapshot.Health?.MineRock, snapshot.MinToolTier?.MineRock) : null), MineRock5 = ((snapshot.MineRock5 != null) ? ConvertDamageableDropTable(snapshot.MineRock5, snapshot.Health?.MineRock5, snapshot.MinToolTier?.MineRock5) : null), TreeBase = ((snapshot.TreeBase != null) ? ConvertDamageableDropTable(snapshot.TreeBase, snapshot.Health?.TreeBase, snapshot.MinToolTier?.TreeBase) : null), TreeLog = ((snapshot.TreeLog != null) ? ConvertDamageableDropTable(snapshot.TreeLog, snapshot.Health?.TreeLog, snapshot.MinToolTier?.TreeLog) : null), Container = ((snapshot.Container != null) ? ConvertDropTable(snapshot.Container) : null), Pickable = ((snapshot.Pickable != null) ? ConvertPickable(snapshot.Pickable) : null), PickableItem = ((snapshot.PickableItem != null) ? ConvertPickableItem(snapshot.PickableItem) : null), Fish = ((snapshot.Fish != null) ? ConvertFish(snapshot.Fish) : null) }; } private static int CompareObjectEntriesForOutput(PrefabConfigurationEntry? left, PrefabConfigurationEntry? right) { if (left == right) { return 0; } if (left == null) { return 1; } if (right == null) { return -1; } int num = GetPrimaryObjectComponentRank(left).CompareTo(GetPrimaryObjectComponentRank(right)); if (num != 0) { return num; } int num2 = GetObjectComponentSignatureMask(left).CompareTo(GetObjectComponentSignatureMask(right)); if (num2 != 0) { return num2; } return string.Compare(left.Prefab, right.Prefab, StringComparison.OrdinalIgnoreCase); } private static int GetPrimaryObjectComponentRank(PrefabConfigurationEntry entry) { if (entry.Container != null) { return 0; } if (entry.Pickable != null) { return 1; } if (entry.PickableItem != null) { return 2; } if (entry.Fish != null) { return 3; } if (entry.MineRock != null) { return 4; } if (entry.MineRock5 != null) { return 5; } if (entry.TreeBase != null) { return 6; } if (entry.TreeLog != null) { return 7; } if (entry.DropOnDestroyed != null) { return 8; } if (entry.Destructible != null) { return 9; } return 10; } private static int GetObjectComponentSignatureMask(PrefabConfigurationEntry entry) { int num = 0; if (entry.Container != null) { num |= 1; } if (entry.Pickable != null) { num |= 2; } if (entry.PickableItem != null) { num |= 4; } if (entry.Fish != null) { num |= 8; } if (entry.MineRock != null) { num |= 0x10; } if (entry.MineRock5 != null) { num |= 0x20; } if (entry.TreeBase != null) { num |= 0x40; } if (entry.TreeLog != null) { num |= 0x80; } if (entry.DropOnDestroyed != null) { num |= 0x100; } if (entry.Destructible != null) { num |= 0x200; } return num; } private static DestructibleDefinition? ConvertDestructible(PrefabSnapshot snapshot) { //IL_0098: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Invalid comparison between Unknown and I4 //IL_00a9: 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) bool flag = snapshot.Health?.Destructible.HasValue ?? false; bool flag2 = snapshot.MinToolTier?.Destructible.HasValue ?? false; if (!flag && !flag2 && snapshot.Destructible == null) { return null; } return new DestructibleDefinition { Health = (flag ? snapshot.Health.Destructible : ((float?)null)), MinToolTier = (flag2 ? snapshot.MinToolTier.Destructible : ((int?)null)), DestructibleType = ((snapshot.Destructible != null && (int)snapshot.Destructible.DestructibleType != 1) ? ((object)snapshot.Destructible.DestructibleType/*cast due to .constrained prefix*/).ToString() : null), SpawnWhenDestroyed = NormalizeReferencePrefabName(snapshot.Destructible?.SpawnWhenDestroyed) }; } private static DropTableDefinition ConvertDropTable(DropTable dropTable) { int num = Math.Max(0, dropTable.m_dropMin); int actualMax = Math.Max(num, dropTable.m_dropMax); List list = (from drop in dropTable.m_drops select new { Name = NormalizeReferencePrefabName(drop.m_item), Drop = drop } into entry where !string.IsNullOrWhiteSpace(entry.Name) select new DropEntryDefinition { Item = entry.Name, Stack = RangeFormatting.FromReference(entry.Drop.m_stackMin, entry.Drop.m_stackMax, 1, 1), Weight = (IsReferenceDefault(entry.Drop.m_weight, 1f) ? ((float?)null) : new float?(entry.Drop.m_weight)), DontScale = (entry.Drop.m_dontScale ? new bool?(true) : ((bool?)null)) }).ToList(); return new DropTableDefinition { Rolls = RangeFormatting.FromReference(num, actualMax, 1, 1), DropChance = (IsReferenceDefault(dropTable.m_dropChance, 1f) ? ((float?)null) : new float?(dropTable.m_dropChance)), OneOfEach = (dropTable.m_oneOfEach ? new bool?(true) : ((bool?)null)), Drops = ((list.Count > 0) ? list : null) }; } private static DamageableDropTableDefinition ConvertDamageableDropTable(DropTable dropTable, float? health, int? minToolTier) { DropTableDefinition dropTableDefinition = ConvertDropTable(dropTable); return new DamageableDropTableDefinition { Health = health, MinToolTier = minToolTier, Rolls = dropTableDefinition.Rolls, DropChance = dropTableDefinition.DropChance, OneOfEach = dropTableDefinition.OneOfEach, Drops = dropTableDefinition.Drops }; } private static PickableDefinition ConvertPickable(PickableSnapshot snapshot) { DropTableDefinition dropTableDefinition = ConvertDropTable(snapshot.ExtraDrops); string text = NormalizeReferencePrefabName(snapshot.ItemPrefab); PickableDropDefinition drop = null; if (!string.IsNullOrWhiteSpace(text) || snapshot.Amount != 1 || snapshot.MinAmountScaled != 0 || snapshot.DontScale) { drop = new PickableDropDefinition { Item = (text ?? ""), Amount = ((snapshot.Amount == 1) ? ((int?)null) : new int?(snapshot.Amount)), MinAmountScaled = ((snapshot.MinAmountScaled == 0) ? ((int?)null) : new int?(snapshot.MinAmountScaled)), DontScale = (snapshot.DontScale ? new bool?(true) : ((bool?)null)) }; } return new PickableDefinition { OverrideName = (string.IsNullOrWhiteSpace(snapshot.OverrideName) ? null : snapshot.OverrideName), Drop = drop, ExtraDrops = (HasReferenceDropTableContent(dropTableDefinition) ? dropTableDefinition : null) }; } private static PickableItemDefinition ConvertPickableItem(PickableItemSnapshot snapshot) { List list = (from item in snapshot.RandomItems select new { Name = NormalizeReferencePrefabName(item.ItemPrefab), Item = item } into entry where !string.IsNullOrWhiteSpace(entry.Name) select new RandomPickableItemDefinition { Item = entry.Name, Stack = RangeFormatting.FromReference(entry.Item.StackMin, entry.Item.StackMax, 1, 1) }).ToList(); string text = NormalizeReferencePrefabName(snapshot.ItemPrefab); return new PickableItemDefinition { RandomDrops = ((list.Count > 0) ? list : null), Drop = ((list.Count == 0 && (!string.IsNullOrWhiteSpace(text) || snapshot.Stack != 1)) ? new PickableItemDropDefinition { Item = (text ?? ""), Stack = ((snapshot.Stack == 1) ? ((int?)null) : new int?(snapshot.Stack)) } : null) }; } private static FishDefinition ConvertFish(FishSnapshot snapshot) { DropTableDefinition dropTableDefinition = ConvertDropTable(snapshot.ExtraDrops); return new FishDefinition { ExtraDrops = (HasReferenceDropTableContent(dropTableDefinition) ? dropTableDefinition : null) }; } private static bool HasReferenceDropTableContent(DropTablePayloadDefinition? definition) { if (definition != null) { IntRangeDefinition? rolls = definition.Rolls; if ((rolls == null || !rolls.HasValues()) && !definition.DropMin.HasValue && !definition.DropMax.HasValue && !definition.DropChance.HasValue && !definition.OneOfEach.HasValue) { if (definition.Drops != null) { return definition.Drops.Count > 0; } return false; } return true; } return false; } private static IntRangeDefinition? GetRollsRange(DropTablePayloadDefinition definition) { return definition.Rolls ?? RangeFormatting.From(definition.DropMin, definition.DropMax ?? definition.DropMin); } private static IntRangeDefinition? GetStackRange(DropEntryDefinition definition) { return definition.Stack ?? RangeFormatting.From(definition.StackMin, definition.StackMax ?? definition.StackMin); } private static IntRangeDefinition? GetStackRange(RandomPickableItemDefinition definition) { return definition.Stack ?? RangeFormatting.From(definition.StackMin, definition.StackMax ?? definition.StackMin); } private static bool IsReferenceDefault(float value, float defaultValue) { return Math.Abs(value - defaultValue) < 0.0001f; } private static string? NormalizeReferencePrefabName(GameObject? prefab) { if (!((Object)(object)prefab == (Object)null)) { return NormalizeReferencePrefabName(((Object)prefab).name); } return null; } private static string? NormalizeReferencePrefabName(string? prefabName) { if (string.IsNullOrWhiteSpace(prefabName)) { return null; } if (!prefabName.StartsWith("JVLmock_", StringComparison.OrdinalIgnoreCase)) { return prefabName; } string text = prefabName.Substring("JVLmock_".Length); if (string.IsNullOrWhiteSpace(text)) { return null; } ZNetScene instance = ZNetScene.instance; if (!((Object)(object)((instance != null) ? instance.GetPrefab(text) : null) != (Object)null)) { ObjectDB instance2 = ObjectDB.instance; if (!((Object)(object)((instance2 != null) ? instance2.GetItemPrefab(text) : null) != (Object)null)) { return null; } } return text; } private static string ResolveObjectOwnerName(string prefabName, Dictionary locationBuckets) { PrefabOwnerResolver.OwnerSnapshot snapshot = PrefabOwnerResolver.GetSnapshot(); string text = (prefabName ?? "").Trim(); if (text.Length > 0 && locationBuckets.TryGetValue(text, out LocationReferenceBucket value)) { List list = (from ownerName in value.Locations.Select(snapshot.GetOwnerName) where !string.Equals(ownerName, "Unknown / Untracked", StringComparison.OrdinalIgnoreCase) select ownerName).Distinct(StringComparer.OrdinalIgnoreCase).ToList(); if (list.Count == 1) { return list[0]; } if (list.Count > 1) { return "Unknown / Untracked"; } } return snapshot.GetOwnerName(text); } private static void WriteLocationReferenceConfigurationFile(string content) { GeneratedArtifactWriter.WriteText(LocationReferenceConfigurationPath, content); } private static void RestoreResourceComponents(GameObject gameObject, PrefabSnapshot snapshot, PrefabConfigurationEntry entry, bool updateRuntimeState) { //IL_00de: Unknown result type (might be due to invalid IL or missing references) Destructible val = default(Destructible); if (HasDestructibleOverride(entry.Destructible) && gameObject.TryGetComponent(ref val) && snapshot.Destructible != null) { if (HasDestructibleHealthOverride(entry.Destructible)) { float? num = snapshot.Health?.Destructible; if (num.HasValue) { float valueOrDefault = num.GetValueOrDefault(); ApplyDestructibleHealth(val, valueOrDefault, updateRuntimeState); } } if (HasDestructibleMinToolTierOverride(entry.Destructible)) { int? num2 = snapshot.MinToolTier?.Destructible; if (num2.HasValue) { int valueOrDefault2 = num2.GetValueOrDefault(); ApplyDestructibleMinToolTier(val, valueOrDefault2); } } if (HasDestructibleComponentStateOverride(entry.Destructible)) { if (!string.IsNullOrWhiteSpace(entry.Destructible?.DestructibleType)) { ApplyDestructibleType(val, snapshot.Destructible.DestructibleType); } if (!updateRuntimeState && HasDestructibleSpawnWhenDestroyedOverride(entry.Destructible)) { val.m_spawnWhenDestroyed = snapshot.Destructible.SpawnWhenDestroyed; } } } DropOnDestroyed val2 = default(DropOnDestroyed); if (UsesLiveDropTableReconcile(LiveObjectComponentKind.DropOnDestroyed) && HasDropTableOverride(entry.DropOnDestroyed) && gameObject.TryGetComponent(ref val2) && snapshot.DropOnDestroyed != null) { val2.m_dropWhenDestroyed = CloneDropTable(snapshot.DropOnDestroyed); } MineRock val3 = default(MineRock); if (HasDamageableOverride(entry.MineRock) && gameObject.TryGetComponent(ref val3)) { if (UsesLiveDropTableReconcile(LiveObjectComponentKind.MineRock) && HasDropTableOverride(entry.MineRock) && snapshot.MineRock != null) { val3.m_dropItems = CloneDropTable(snapshot.MineRock); } if (HasDamageableHealthOverride(entry.MineRock)) { float? num = snapshot.Health?.MineRock; if (num.HasValue) { float valueOrDefault3 = num.GetValueOrDefault(); ApplyMineRockHealth(val3, valueOrDefault3, updateRuntimeState); } } if (HasDamageableMinToolTierOverride(entry.MineRock)) { int? num2 = snapshot.MinToolTier?.MineRock; if (num2.HasValue) { int valueOrDefault4 = num2.GetValueOrDefault(); val3.m_minToolTier = valueOrDefault4; } } } MineRock5 val4 = default(MineRock5); if (HasDamageableOverride(entry.MineRock5) && gameObject.TryGetComponent(ref val4)) { if (UsesLiveDropTableReconcile(LiveObjectComponentKind.MineRock5) && HasDropTableOverride(entry.MineRock5) && snapshot.MineRock5 != null) { val4.m_dropItems = CloneDropTable(snapshot.MineRock5); } if (HasDamageableHealthOverride(entry.MineRock5)) { float? num = snapshot.Health?.MineRock5; if (num.HasValue) { float valueOrDefault5 = num.GetValueOrDefault(); ApplyMineRock5Health(val4, valueOrDefault5, updateRuntimeState); } } if (HasDamageableMinToolTierOverride(entry.MineRock5)) { int? num2 = snapshot.MinToolTier?.MineRock5; if (num2.HasValue) { int valueOrDefault6 = num2.GetValueOrDefault(); val4.m_minToolTier = valueOrDefault6; } } } TreeBase val5 = default(TreeBase); if (HasDamageableOverride(entry.TreeBase) && gameObject.TryGetComponent(ref val5)) { if (UsesLiveDropTableReconcile(LiveObjectComponentKind.TreeBase) && HasDropTableOverride(entry.TreeBase) && snapshot.TreeBase != null) { val5.m_dropWhenDestroyed = CloneDropTable(snapshot.TreeBase); } if (HasDamageableHealthOverride(entry.TreeBase)) { float? num = snapshot.Health?.TreeBase; if (num.HasValue) { float valueOrDefault7 = num.GetValueOrDefault(); ApplyTreeBaseHealth(val5, valueOrDefault7, updateRuntimeState); } } if (HasDamageableMinToolTierOverride(entry.TreeBase)) { int? num2 = snapshot.MinToolTier?.TreeBase; if (num2.HasValue) { int valueOrDefault8 = num2.GetValueOrDefault(); val5.m_minToolTier = valueOrDefault8; } } } TreeLog val6 = default(TreeLog); if (!HasDamageableOverride(entry.TreeLog) || !gameObject.TryGetComponent(ref val6)) { return; } if (UsesLiveDropTableReconcile(LiveObjectComponentKind.TreeLog) && HasDropTableOverride(entry.TreeLog) && snapshot.TreeLog != null) { val6.m_dropWhenDestroyed = CloneDropTable(snapshot.TreeLog); } if (HasDamageableHealthOverride(entry.TreeLog)) { float? num = snapshot.Health?.TreeLog; if (num.HasValue) { float valueOrDefault9 = num.GetValueOrDefault(); ApplyTreeLogHealth(val6, valueOrDefault9, updateRuntimeState); } } if (HasDamageableMinToolTierOverride(entry.TreeLog)) { int? num2 = snapshot.MinToolTier?.TreeLog; if (num2.HasValue) { int valueOrDefault10 = num2.GetValueOrDefault(); val6.m_minToolTier = valueOrDefault10; } } } private static void ApplyResourceComponents(GameObject gameObject, PrefabConfigurationEntry entry, CompiledObjectDropRule? compiledRule, string contextRoot, bool updateRuntimeState) { //IL_0150: Unknown result type (might be due to invalid IL or missing references) if (HasDestructibleOverride(entry.Destructible)) { Destructible val = default(Destructible); if (!gameObject.TryGetComponent(ref val)) { WarnMissingComponent(contextRoot + "/Destructible", "Destructible"); } else { float normalizedHealth; if (compiledRule != null && compiledRule.Destructible?.HasHealthOverride == true) { ApplyDestructibleHealth(val, compiledRule.Destructible.Health, updateRuntimeState); } else if (HasDestructibleHealthOverride(entry.Destructible) && TryGetConfiguredHealth(entry.Destructible.Health.Value, contextRoot + "/Destructible/Health", out normalizedHealth)) { ApplyDestructibleHealth(val, normalizedHealth, updateRuntimeState); } int normalizedMinToolTier; if (compiledRule != null && compiledRule.Destructible?.HasMinToolTierOverride == true) { ApplyDestructibleMinToolTier(val, compiledRule.Destructible.MinToolTier); } else if (HasDestructibleMinToolTierOverride(entry.Destructible) && TryGetConfiguredMinToolTier(entry.Destructible.MinToolTier.Value, contextRoot + "/Destructible/minToolTier", out normalizedMinToolTier)) { ApplyDestructibleMinToolTier(val, normalizedMinToolTier); } if (compiledRule?.Destructible != null) { if (compiledRule.Destructible.HasDestructibleTypeOverride) { ApplyDestructibleType(val, compiledRule.Destructible.DestructibleType); } if (!updateRuntimeState && compiledRule.Destructible.HasSpawnWhenDestroyedOverride) { val.m_spawnWhenDestroyed = compiledRule.Destructible.SpawnWhenDestroyed; } } else if (HasDestructibleComponentStateOverride(entry.Destructible)) { ApplyDestructibleDefinition(val, entry.Destructible, contextRoot + "/Destructible", !updateRuntimeState); } } } DropOnDestroyed val2 = default(DropOnDestroyed); if (HasDropTableOverride(entry.DropOnDestroyed) && !gameObject.TryGetComponent(ref val2)) { WarnMissingComponent(contextRoot + "/DropOnDestroyed", "DropOnDestroyed"); } if (HasDamageableOverride(entry.MineRock)) { MineRock val3 = default(MineRock); if (!gameObject.TryGetComponent(ref val3)) { WarnMissingComponent(contextRoot + "/MineRock", "MineRock"); } else { float normalizedHealth2; if (compiledRule != null && compiledRule.MineRockScalars?.HasHealthOverride == true) { ApplyMineRockHealth(val3, compiledRule.MineRockScalars.Health, updateRuntimeState); } else if (HasDamageableHealthOverride(entry.MineRock) && TryGetConfiguredHealth(entry.MineRock.Health.Value, contextRoot + "/MineRock/Health", out normalizedHealth2)) { ApplyMineRockHealth(val3, normalizedHealth2, updateRuntimeState); } int normalizedMinToolTier2; if (compiledRule != null && compiledRule.MineRockScalars?.HasMinToolTierOverride == true) { val3.m_minToolTier = compiledRule.MineRockScalars.MinToolTier; } else if (HasDamageableMinToolTierOverride(entry.MineRock) && TryGetConfiguredMinToolTier(entry.MineRock.MinToolTier.Value, contextRoot + "/MineRock/minToolTier", out normalizedMinToolTier2)) { val3.m_minToolTier = normalizedMinToolTier2; } } } if (HasDamageableOverride(entry.MineRock5)) { MineRock5 val4 = default(MineRock5); if (!gameObject.TryGetComponent(ref val4)) { WarnMissingComponent(contextRoot + "/MineRock5", "MineRock5"); } else { float normalizedHealth3; if (compiledRule != null && compiledRule.MineRock5Scalars?.HasHealthOverride == true) { ApplyMineRock5Health(val4, compiledRule.MineRock5Scalars.Health, updateRuntimeState); } else if (HasDamageableHealthOverride(entry.MineRock5) && TryGetConfiguredHealth(entry.MineRock5.Health.Value, contextRoot + "/MineRock5/Health", out normalizedHealth3)) { ApplyMineRock5Health(val4, normalizedHealth3, updateRuntimeState); } int normalizedMinToolTier3; if (compiledRule != null && compiledRule.MineRock5Scalars?.HasMinToolTierOverride == true) { val4.m_minToolTier = compiledRule.MineRock5Scalars.MinToolTier; } else if (HasDamageableMinToolTierOverride(entry.MineRock5) && TryGetConfiguredMinToolTier(entry.MineRock5.MinToolTier.Value, contextRoot + "/MineRock5/minToolTier", out normalizedMinToolTier3)) { val4.m_minToolTier = normalizedMinToolTier3; } } } if (HasDamageableOverride(entry.TreeBase)) { TreeBase val5 = default(TreeBase); if (!gameObject.TryGetComponent(ref val5)) { WarnMissingComponent(contextRoot + "/TreeBase", "TreeBase"); } else { float normalizedHealth4; if (compiledRule != null && compiledRule.TreeBaseScalars?.HasHealthOverride == true) { ApplyTreeBaseHealth(val5, compiledRule.TreeBaseScalars.Health, updateRuntimeState); } else if (HasDamageableHealthOverride(entry.TreeBase) && TryGetConfiguredHealth(entry.TreeBase.Health.Value, contextRoot + "/TreeBase/Health", out normalizedHealth4)) { ApplyTreeBaseHealth(val5, normalizedHealth4, updateRuntimeState); } int normalizedMinToolTier4; if (compiledRule != null && compiledRule.TreeBaseScalars?.HasMinToolTierOverride == true) { val5.m_minToolTier = compiledRule.TreeBaseScalars.MinToolTier; } else if (HasDamageableMinToolTierOverride(entry.TreeBase) && TryGetConfiguredMinToolTier(entry.TreeBase.MinToolTier.Value, contextRoot + "/TreeBase/minToolTier", out normalizedMinToolTier4)) { val5.m_minToolTier = normalizedMinToolTier4; } } } if (!HasDamageableOverride(entry.TreeLog)) { return; } TreeLog val6 = default(TreeLog); if (!gameObject.TryGetComponent(ref val6)) { WarnMissingComponent(contextRoot + "/TreeLog", "TreeLog"); return; } float normalizedHealth5; if (compiledRule != null && compiledRule.TreeLogScalars?.HasHealthOverride == true) { ApplyTreeLogHealth(val6, compiledRule.TreeLogScalars.Health, updateRuntimeState); } else if (HasDamageableHealthOverride(entry.TreeLog) && TryGetConfiguredHealth(entry.TreeLog.Health.Value, contextRoot + "/TreeLog/Health", out normalizedHealth5)) { ApplyTreeLogHealth(val6, normalizedHealth5, updateRuntimeState); } int normalizedMinToolTier5; if (compiledRule != null && compiledRule.TreeLogScalars?.HasMinToolTierOverride == true) { val6.m_minToolTier = compiledRule.TreeLogScalars.MinToolTier; } else if (HasDamageableMinToolTierOverride(entry.TreeLog) && TryGetConfiguredMinToolTier(entry.TreeLog.MinToolTier.Value, contextRoot + "/TreeLog/minToolTier", out normalizedMinToolTier5)) { val6.m_minToolTier = normalizedMinToolTier5; } } private static string BuildFullScaffoldConfigurationTemplate() { StringBuilder stringBuilder = new StringBuilder(); bool flag = false; foreach (PrefabOwnerSection item in BuildConfigurationTemplate()) { foreach (PrefabConfigurationEntry entry in item.Entries) { if (flag) { AppendScaffoldBlankLine(stringBuilder); } AppendScaffoldObjectEntry(stringBuilder, entry); flag = true; } } if (!flag) { return "[]" + Environment.NewLine; } return stringBuilder.ToString(); } private static void AppendScaffoldObjectEntry(StringBuilder builder, PrefabConfigurationEntry entry) { AppendScaffoldListEntryLine(builder, 0, "prefab", entry.Prefab); AppendScaffoldLine(builder, 1, "enabled: " + FormatYamlBool(entry.Enabled)); AppendScaffoldConditionsBlock(builder, 1); if (entry.DropOnDestroyed != null) { AppendScaffoldDropTableBlock(builder, 1, "dropOnDestroyed", entry.DropOnDestroyed); } if (entry.MineRock != null) { AppendScaffoldDamageableDropTableBlock(builder, 1, "mineRock", entry.MineRock); } if (entry.MineRock5 != null) { AppendScaffoldDamageableDropTableBlock(builder, 1, "mineRock5", entry.MineRock5); } if (entry.TreeBase != null) { AppendScaffoldDamageableDropTableBlock(builder, 1, "treeBase", entry.TreeBase); } if (entry.TreeLog != null) { AppendScaffoldDamageableDropTableBlock(builder, 1, "treeLog", entry.TreeLog); } if (entry.Container != null) { AppendScaffoldDropTableBlock(builder, 1, "container", entry.Container); } if (entry.PickableItem != null) { AppendScaffoldPickableItemBlock(builder, entry.PickableItem); } if (entry.Pickable != null) { AppendScaffoldPickableBlock(builder, entry.Pickable); } if (entry.Fish != null) { AppendScaffoldFishBlock(builder, entry.Fish); } if (entry.Destructible != null) { AppendScaffoldDestructibleBlock(builder, entry.Destructible); } } private static void AppendScaffoldDamageableDropTableBlock(StringBuilder builder, int indent, string blockName, DamageableDropTableDefinition definition) { AppendScaffoldLine(builder, indent, blockName + ":"); AppendScaffoldLine(builder, indent + 1, "health: " + FormatYamlFloat(definition.Health ?? 1f)); AppendScaffoldLine(builder, indent + 1, $"minToolTier: {definition.MinToolTier.GetValueOrDefault()}"); AppendScaffoldDropTableValueLines(builder, indent + 1, definition); } private static void AppendScaffoldDropTableBlock(StringBuilder builder, int indent, string blockName, DropTableDefinition definition) { AppendScaffoldLine(builder, indent, blockName + ":"); AppendScaffoldDropTableValueLines(builder, indent + 1, definition); } private static void AppendScaffoldDropTablePayloadBlock(StringBuilder builder, int indent, string blockName, DropTablePayloadDefinition definition) { AppendScaffoldLine(builder, indent, blockName + ":"); AppendScaffoldDropTableValueLines(builder, indent + 1, definition); } private static void AppendScaffoldDropTableValueLines(StringBuilder builder, int indent, DropTablePayloadDefinition definition) { AppendScaffoldLine(builder, indent, "rolls: " + RangeFormatting.FormatInlineObject(GetRollsRange(definition) ?? RangeFormatting.From(1, 1))); AppendScaffoldLine(builder, indent, "dropChance: " + FormatYamlFloat(definition.DropChance ?? 1f)); AppendScaffoldLine(builder, indent, "oneOfEach: " + FormatYamlBool(definition.OneOfEach == true)); if (definition.Drops != null && definition.Drops.Count > 0) { AppendScaffoldLine(builder, indent, "drops:"); { foreach (DropEntryDefinition drop in definition.Drops) { AppendScaffoldDropEntry(builder, indent + 1, drop); } return; } } AppendScaffoldLine(builder, indent, "drops: []"); } private static void AppendScaffoldDropEntry(StringBuilder builder, int indent, DropEntryDefinition definition) { AppendScaffoldListEntryLine(builder, indent, "item", definition.Item); AppendScaffoldLine(builder, indent + 1, "stack: " + RangeFormatting.FormatInlineObject(GetStackRange(definition) ?? RangeFormatting.From(1, 1))); AppendScaffoldLine(builder, indent + 1, "weight: " + FormatYamlFloat(definition.Weight ?? 1f)); AppendScaffoldLine(builder, indent + 1, "dontScale: " + FormatYamlBool(definition.DontScale == true)); } private static void AppendScaffoldDestructibleBlock(StringBuilder builder, DestructibleDefinition definition) { //IL_006b: Unknown result type (might be due to invalid IL or missing references) AppendScaffoldLine(builder, 1, "destructible:"); AppendScaffoldLine(builder, 2, "health: " + FormatYamlFloat(definition.Health ?? 1f)); AppendScaffoldLine(builder, 2, $"minToolTier: {definition.MinToolTier.GetValueOrDefault()}"); AppendScaffoldLine(builder, 2, "destructibleType: " + FormatYamlString(definition.DestructibleType ?? ((object)(DestructibleType)1/*cast due to .constrained prefix*/).ToString())); AppendScaffoldStringLine(builder, 2, "spawnWhenDestroyed", definition.SpawnWhenDestroyed); } private static void AppendScaffoldPickableBlock(StringBuilder builder, PickableDefinition definition) { AppendScaffoldLine(builder, 1, "pickable:"); AppendScaffoldStringLine(builder, 2, "overrideName", definition.OverrideName); AppendScaffoldLine(builder, 2, "drop:"); AppendScaffoldStringLine(builder, 3, "item", definition.Drop?.Item); AppendScaffoldLine(builder, 3, $"amount: {definition.Drop?.Amount ?? 1}"); AppendScaffoldLine(builder, 3, $"minAmountScaled: {(definition.Drop?.MinAmountScaled).GetValueOrDefault()} # Minimum final amount after Game.ScaleDrops; ignored when dontScale is true."); AppendScaffoldLine(builder, 3, "dontScale: " + FormatYamlBool(definition.Drop?.DontScale == true)); AppendScaffoldDropTablePayloadBlock(builder, 2, "extraDrops", definition.ExtraDrops ?? new DropTablePayloadDefinition()); } private static void AppendScaffoldPickableItemBlock(StringBuilder builder, PickableItemDefinition definition) { AppendScaffoldLine(builder, 1, "pickableItem:"); if (definition.RandomDrops != null && definition.RandomDrops.Count > 0) { AppendScaffoldLine(builder, 2, "randomDrops:"); foreach (RandomPickableItemDefinition randomDrop in definition.RandomDrops) { AppendScaffoldListEntryLine(builder, 3, "item", randomDrop.Item); AppendScaffoldLine(builder, 4, "stack: " + RangeFormatting.FormatInlineObject(GetStackRange(randomDrop) ?? RangeFormatting.From(1, 1))); if (randomDrop.Weight.HasValue) { AppendScaffoldLine(builder, 4, "weight: " + FormatYamlFloat(randomDrop.Weight.Value)); } } } else { AppendScaffoldLine(builder, 2, "randomDrops: []"); } if (definition.Drop != null) { AppendScaffoldLine(builder, 2, "drop:"); AppendScaffoldStringLine(builder, 3, "item", definition.Drop.Item); AppendScaffoldLine(builder, 3, $"stack: {definition.Drop.Stack ?? 1}"); } else { AppendScaffoldLine(builder, 2, "drop:"); AppendScaffoldLine(builder, 3, "item: null"); AppendScaffoldLine(builder, 3, "stack: 1"); } } private static void AppendScaffoldFishBlock(StringBuilder builder, FishDefinition definition) { AppendScaffoldLine(builder, 1, "fish:"); AppendScaffoldDropTablePayloadBlock(builder, 2, "extraDrops", definition.ExtraDrops ?? new DropTablePayloadDefinition()); } private static void AppendScaffoldLine(StringBuilder builder, int indent, string text) { builder.Append(' ', indent * 2); builder.AppendLine(text); } private static void AppendScaffoldBlankLine(StringBuilder builder) { builder.AppendLine(); } private static void AppendScaffoldConditionsBlock(StringBuilder builder, int indent) { AppendScaffoldLine(builder, indent, "conditions:"); AppendScaffoldLine(builder, indent + 1, "altitude: null"); AppendScaffoldLine(builder, indent + 1, "distanceFromCenter: null"); AppendScaffoldLine(builder, indent + 1, "biomes: []"); AppendScaffoldLine(builder, indent + 1, "locations: []"); AppendScaffoldLine(builder, indent + 1, "timeOfDay: null"); AppendScaffoldLine(builder, indent + 1, "requiredEnvironments: []"); AppendScaffoldLine(builder, indent + 1, "requiredGlobalKeys: []"); AppendScaffoldLine(builder, indent + 1, "forbiddenGlobalKeys: []"); AppendScaffoldLine(builder, indent + 1, "inForest: null"); AppendScaffoldLine(builder, indent + 1, "inDungeon: null"); AppendScaffoldLine(builder, indent + 1, "insidePlayerBase: null"); } private static void AppendScaffoldStringLine(StringBuilder builder, int indent, string key, string? value) { if (value == null) { AppendScaffoldLine(builder, indent, key + ": null"); } else { AppendScaffoldLine(builder, indent, key + ": " + FormatYamlString(value)); } } private static void AppendScaffoldListEntryLine(StringBuilder builder, int indent, string key, string? value) { if (value == null) { AppendScaffoldLine(builder, indent, "- " + key + ": null"); } else { AppendScaffoldLine(builder, indent, "- " + key + ": " + FormatYamlString(value)); } } private static string BuildPrimaryOverrideConfigurationTemplate() { StringBuilder stringBuilder = new StringBuilder(); CommentedYamlTemplateSupport.AppendTemplateComment(stringBuilder, "Any file named " + PluginSettingsFacade.GetYamlDomainSupplementalPrefix("object") + "*.yml or " + PluginSettingsFacade.GetYamlDomainSupplementalPrefix("object") + "*.yaml is also loaded # ex) " + PluginSettingsFacade.GetYamlDomainFilePrefix("object") + "_rand1.yml, " + PluginSettingsFacade.GetYamlDomainFilePrefix("object") + "_rand2.yaml"); CommentedYamlTemplateSupport.AppendTemplateComment(stringBuilder, "Use " + PluginSettingsFacade.GetYamlDomainFilePrefix("object") + ".reference.yml to look up real prefab names and reference values, " + PluginSettingsFacade.GetYamlDomainFilePrefix("object") + ".locations.reference.yml to see which location roots include a given object prefab, and run `dns:full object` for exhaustive field examples"); CommentedYamlTemplateSupport.AppendTemplateComment(stringBuilder, "Matching drop-table component blocks merge together. If any block matches, vanilla rows for that component are replaced by the union of matching custom rows."); CommentedYamlTemplateSupport.AppendTemplateComment(stringBuilder, "A conditionless custom entry becomes the custom default for every populated component in that entry."); CommentedYamlTemplateSupport.AppendTemplateComment(stringBuilder, "Piece-based prefabs are world-only. Player-built instances are not overridden."); CommentedYamlTemplateSupport.AppendTemplateBlankLine(stringBuilder); CommentedYamlTemplateSupport.AppendTemplateComment(stringBuilder, "dropOnDestroyed"); CommentedYamlTemplateSupport.AppendTemplateBlankLine(stringBuilder); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 0, "- prefab: goblin_totempole"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 1, "enabled: true"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 1, "conditions: # If these conditions fail, this entry is ignored # Vanilla rows are used only when no custom drop-table entry for that component matches"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 2, "altitude: null # ex) -1000~1000 # Range in world-height meters"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 2, "distanceFromCenter: null # ex) 0~10000 # Range in meters from the world center"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 2, "biomes: [] # ex) [BlackForest, Mistlands] # Allowed biomes # EWD custom biome names also work when EWD is installed"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 2, "locations: [] # ex) [Hildir_camp] # Allowed location prefab names"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 2, "timeOfDay: null # ex) [night] # [day, night]"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 2, "requiredEnvironments: [] # ex) [Rain] # Allowed environment names"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 2, "requiredGlobalKeys: [] # ex) [defeated_gdking] # Required global keys"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 2, "forbiddenGlobalKeys: [] # ex) [nomap] # Forbidden global keys"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 2, "inForest: null # ex) true = forest only # false = outside forest only # null or no field allows both"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 2, "inDungeon: null # ex) true = dungeon only # false = overworld only # null or no field allows both"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 2, "insidePlayerBase: null # ex) true = near player base only # false = away from player base only # null or no field allows both"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 1, "dropOnDestroyed:"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 2, "rolls: 1~1 # ex) 1~3 # Range of successful rolls from this table"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 2, "dropChance: 1 # Chance from 0 to 1 that this table rolls at all"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 2, "oneOfEach: false # True lets each entry roll at most once per table roll"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 2, "drops: # Set drops: [] to disable a drop table"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 2, "- item: null # ex) Wood # Required item prefab name"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 3, "stack: 1~1 # ex) 1~2 # Range of stack size"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 3, "weight: 1 # Relative weight versus other entries in the same table"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 3, "dontScale: false # True skips the game's built-in drop scaling for this entry"); CommentedYamlTemplateSupport.AppendTemplateBlankLine(stringBuilder); CommentedYamlTemplateSupport.AppendTemplateComment(stringBuilder, "mineRock, mineRock5, treeBase, and treeLog"); CommentedYamlTemplateSupport.AppendTemplateBlankLine(stringBuilder); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 0, "- prefab: MineRock_Copper"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 1, "enabled: true"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 1, "conditions: {}"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 1, "mineRock:"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 2, "health: null # ex) 1000"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 2, "minToolTier: 0 # ex) tier 0 AxeStone/PickaxeAntler, tier 1 AxeFlint/PickaxeBronze, tier 2 AxeBronze/PickaxeIron, tier 3 PickaxeBlackMetal, tier 4 AxeBlackMetal/AxeJotunBane, tier 5 BatteringRam, tier 6 AxeBerzerkr"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 2, "rolls: 1~1"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 2, "dropChance: 1"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 2, "oneOfEach: false"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 2, "drops:"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 2, "- item: null # ex) Stone"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 3, "stack: 1~1 # ex) 1~3 # Range of stack size"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 3, "weight: 1"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 3, "dontScale: false"); CommentedYamlTemplateSupport.AppendTemplateBlankLine(stringBuilder); CommentedYamlTemplateSupport.AppendTemplateComment(stringBuilder, "container"); CommentedYamlTemplateSupport.AppendTemplateBlankLine(stringBuilder); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 0, "- prefab: TreasureChest_meadows"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 1, "enabled: true"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 1, "conditions: {}"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 1, "container:"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 2, "rolls: 1~1"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 2, "dropChance: 1"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 2, "oneOfEach: false"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 2, "drops:"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 2, "- item: null # ex) Coins # Required item prefab name"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 3, "stack: 1~1 # ex) 10~20 # Range of stack size"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 3, "weight: 1"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 3, "dontScale: false"); CommentedYamlTemplateSupport.AppendTemplateBlankLine(stringBuilder); CommentedYamlTemplateSupport.AppendTemplateComment(stringBuilder, "pickableItem # Use either randomDrops or drop # If both are set, randomDrops takes precedence"); CommentedYamlTemplateSupport.AppendTemplateBlankLine(stringBuilder); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 0, "- prefab: Pickable_DolmenTreasure"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 1, "enabled: true"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 1, "conditions: {}"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 1, "pickableItem:"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 2, "randomDrops:"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 2, "- item: null # ex) Coins"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 3, "stack: 1~1 # ex) 1~3 # Range of stack size"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 3, "weight: 1"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 2, "drop:"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 3, "item: null # ex) Coins"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 3, "stack: 1"); CommentedYamlTemplateSupport.AppendTemplateBlankLine(stringBuilder); CommentedYamlTemplateSupport.AppendTemplateComment(stringBuilder, "pickable"); CommentedYamlTemplateSupport.AppendTemplateBlankLine(stringBuilder); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 0, "- prefab: BlueberryBush"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 1, "enabled: true"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 1, "conditions: {}"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 1, "pickable:"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 2, "overrideName: null # Optional display name override"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 2, "drop:"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 3, "item: null # ex) Blueberries"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 3, "amount: 1"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 3, "minAmountScaled: 0 # Minimum final amount after Game.ScaleDrops # ignored when dontScale is true"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 3, "dontScale: false"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 2, "extraDrops:"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 3, "rolls: 1~1"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 3, "dropChance: 1"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 3, "oneOfEach: false"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 3, "drops:"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 3, "- item: null # ex) Wood"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 4, "stack: 1~1 # ex) 1~3 # Range of stack size"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 4, "weight: 1"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 4, "dontScale: false"); CommentedYamlTemplateSupport.AppendTemplateBlankLine(stringBuilder); CommentedYamlTemplateSupport.AppendTemplateComment(stringBuilder, "fish"); CommentedYamlTemplateSupport.AppendTemplateBlankLine(stringBuilder); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 0, "- prefab: Fish2"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 1, "enabled: true"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 1, "conditions: {}"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 1, "fish:"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 2, "extraDrops:"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 3, "rolls: 1~1"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 3, "dropChance: 1"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 3, "oneOfEach: false"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 3, "drops:"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 3, "- item: null # ex) Amber"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 4, "stack: 1~1 # ex) 1~3 # Range of stack size"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 4, "weight: 1"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 4, "dontScale: false"); CommentedYamlTemplateSupport.AppendTemplateBlankLine(stringBuilder); CommentedYamlTemplateSupport.AppendTemplateComment(stringBuilder, "destructible"); CommentedYamlTemplateSupport.AppendTemplateBlankLine(stringBuilder); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 0, "- prefab: CloudberryBush"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 1, "enabled: true"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 1, "conditions: {}"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 1, "destructible:"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 2, "health: null # ex) 80"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 2, "minToolTier: 0"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 2, "destructibleType: Default # Values: None, Default, Tree, Character, Everything"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 2, "spawnWhenDestroyed: null # ex) Cloudberry # Optional direct spawn prefab"); CommentedYamlTemplateSupport.AppendTemplateBlankLine(stringBuilder); return stringBuilder.ToString(); } private static void RefreshVneiCompatibility(Dictionary previousVneiEntrySignatures) { VneiCompatibility.RefreshObjectPrefabs(BuildDirtyPrefabs(previousVneiEntrySignatures, BuildVneiEntrySignaturesByPrefab())); } private static PrefabConfigurationEntry? CreateVneiProjectionEntry(PrefabConfigurationEntry? entry) { if (entry == null || !entry.Enabled || string.IsNullOrWhiteSpace(entry.Prefab)) { return null; } PrefabConfigurationEntry prefabConfigurationEntry = ConfigurationEntryCloneSupport.ClonePrefabConfigurationEntry(entry); prefabConfigurationEntry.DropOnDestroyed = (HasDropTableOverride(entry.DropOnDestroyed) ? prefabConfigurationEntry.DropOnDestroyed : null); prefabConfigurationEntry.MineRock = (HasDamageableOverride(entry.MineRock) ? prefabConfigurationEntry.MineRock : null); prefabConfigurationEntry.MineRock5 = (HasDamageableOverride(entry.MineRock5) ? prefabConfigurationEntry.MineRock5 : null); prefabConfigurationEntry.TreeBase = (HasDamageableOverride(entry.TreeBase) ? prefabConfigurationEntry.TreeBase : null); prefabConfigurationEntry.TreeLog = (HasDamageableOverride(entry.TreeLog) ? prefabConfigurationEntry.TreeLog : null); prefabConfigurationEntry.Container = (HasDropTableOverride(entry.Container) ? prefabConfigurationEntry.Container : null); prefabConfigurationEntry.Pickable = (HasPickableOverride(entry.Pickable) ? prefabConfigurationEntry.Pickable : null); prefabConfigurationEntry.PickableItem = null; prefabConfigurationEntry.Fish = (HasFishOverride(entry.Fish) ? prefabConfigurationEntry.Fish : null); prefabConfigurationEntry.Destructible = (HasDestructibleOverride(entry.Destructible) ? prefabConfigurationEntry.Destructible : null); if (prefabConfigurationEntry.DropOnDestroyed == null && prefabConfigurationEntry.MineRock == null && prefabConfigurationEntry.MineRock5 == null && prefabConfigurationEntry.TreeBase == null && prefabConfigurationEntry.TreeLog == null && prefabConfigurationEntry.Container == null && prefabConfigurationEntry.Pickable == null && prefabConfigurationEntry.Fish == null && prefabConfigurationEntry.Destructible == null) { return null; } return prefabConfigurationEntry; } private static void RebuildVneiDisplayEntries(IEnumerable entries) { RebuildVneiDisplayEntries(entries, RuntimeState.VneiEntriesByPrefab); } private static void RebuildVneiDisplayEntries(IEnumerable entries, Dictionary> vneiEntriesByPrefab) { vneiEntriesByPrefab.Clear(); foreach (PrefabConfigurationEntry item in entries ?? Enumerable.Empty()) { PrefabConfigurationEntry prefabConfigurationEntry = CreateVneiProjectionEntry(item); if (prefabConfigurationEntry != null) { if (!vneiEntriesByPrefab.TryGetValue(prefabConfigurationEntry.Prefab, out List value)) { value = new List(); vneiEntriesByPrefab[prefabConfigurationEntry.Prefab] = value; } value.Add(prefabConfigurationEntry); } } } private static Dictionary BuildVneiEntrySignaturesByPrefab() { return DomainEntrySignatureSupport.BuildSignaturesByKey(RuntimeState.VneiEntriesByPrefab, (List entries) => NetworkPayloadSyncSupport.ComputeObjectConfigurationSignature(entries.OrderBy((PrefabConfigurationEntry entry) => entry.RuleId, StringComparer.Ordinal).ToList())); } private static List? GetVneiEntries(string prefabName) { if (RuntimeState.VneiEntriesByPrefab.TryGetValue(prefabName ?? "", out List value)) { return value; } if (!RuntimeState.ActiveEntriesByPrefab.TryGetValue(prefabName ?? "", out List value2)) { return null; } return value2; } internal static bool HasVneiRelevantContainerOverride(string prefabName) { lock (Sync) { return HasRelevantVneiOverride(prefabName, (PrefabConfigurationEntry entry) => HasDropTableOverride(entry.Container)); } } internal static bool HasVneiRelevantDropOnDestroyedOverride(string prefabName) { lock (Sync) { return HasRelevantVneiOverride(prefabName, (PrefabConfigurationEntry entry) => HasDropTableOverride(entry.DropOnDestroyed)); } } internal static bool HasVneiRelevantMineRockOverride(string prefabName) { lock (Sync) { return HasRelevantVneiOverride(prefabName, (PrefabConfigurationEntry entry) => HasDropTableOverride(entry.MineRock)); } } internal static bool HasVneiRelevantMineRock5Override(string prefabName) { lock (Sync) { return HasRelevantVneiOverride(prefabName, (PrefabConfigurationEntry entry) => HasDropTableOverride(entry.MineRock5)); } } internal static bool HasVneiRelevantPickableOverride(string prefabName) { lock (Sync) { return HasRelevantVneiOverride(prefabName, (PrefabConfigurationEntry entry) => entry.Pickable != null && (HasPickableDropOverride(entry.Pickable.Drop) || HasDropTableOverride(entry.Pickable.ExtraDrops))); } } internal static bool HasVneiRelevantFishOverride(string prefabName) { lock (Sync) { return HasRelevantVneiOverride(prefabName, (PrefabConfigurationEntry entry) => HasDropTableOverride(entry.Fish?.ExtraDrops)); } } internal static bool HasVneiRelevantDestructibleOverride(string prefabName) { lock (Sync) { return HasRelevantVneiOverride(prefabName, (PrefabConfigurationEntry entry) => !string.IsNullOrWhiteSpace(entry.Destructible?.SpawnWhenDestroyed)); } } internal static bool HasVneiRelevantTreeBaseOverride(string prefabName) { lock (Sync) { return HasRelevantVneiOverride(prefabName, (PrefabConfigurationEntry entry) => HasDropTableOverride(entry.TreeBase) || HasDropTableOverride(entry.TreeLog) || HasDropTableOverride(entry.DropOnDestroyed)); } } internal static bool TryGetVneiDisplayForDropTable(GameObject prefab, DropTable dropTable, out List results) { lock (Sync) { results = new List(); if ((Object)(object)prefab == (Object)null || dropTable == null) { return false; } Container val = default(Container); if (prefab.TryGetComponent(ref val) && val.m_defaultItems == dropTable) { return TryGetVneiDisplayForContainer(prefab, out results); } DropOnDestroyed val2 = default(DropOnDestroyed); if (prefab.TryGetComponent(ref val2) && val2.m_dropWhenDestroyed == dropTable) { return TryGetVneiDropTableDisplay(prefab, (PrefabSnapshot snapshot) => snapshot.DropOnDestroyed, (PrefabConfigurationEntry entry) => entry.DropOnDestroyed, (DropTableDefinition payload) => payload, "dropOnDestroyed", 1, out results); } MineRock val3 = default(MineRock); if (prefab.TryGetComponent(ref val3) && val3.m_dropItems == dropTable) { return TryGetVneiDropTableDisplay(prefab, (PrefabSnapshot snapshot) => snapshot.MineRock, (PrefabConfigurationEntry entry) => entry.MineRock, (DamageableDropTableDefinition payload) => payload, "mineRock", 1, out results); } MineRock5 val4 = default(MineRock5); if (prefab.TryGetComponent(ref val4) && val4.m_dropItems == dropTable) { return TryGetVneiDropTableDisplay(prefab, (PrefabSnapshot snapshot) => snapshot.MineRock5, (PrefabConfigurationEntry entry) => entry.MineRock5, (DamageableDropTableDefinition payload) => payload, "mineRock5", 1, out results); } Fish val5 = default(Fish); if (prefab.TryGetComponent(ref val5) && val5.m_extraDrops == dropTable) { return TryGetVneiDisplayForFish(val5, out results); } return false; } } internal static bool TryGetVneiDisplayForContainer(GameObject prefab, out List results) { lock (Sync) { return TryGetVneiDropTableDisplay(prefab, (PrefabSnapshot snapshot) => snapshot.Container, (PrefabConfigurationEntry entry) => entry.Container, (DropTableDefinition payload) => payload, "container", 1, out results); } } internal static bool TryGetVneiDisplayForPickable(GameObject prefab, Pickable pickable, out List results) { lock (Sync) { results = new List(); if ((Object)(object)prefab == (Object)null || (Object)(object)pickable == (Object)null) { return false; } string prefabName = GetPrefabName(prefab); List vneiEntries = GetVneiEntries(prefabName); SnapshotState.SnapshotsByPrefab.TryGetValue(prefabName, out PrefabSnapshot value); PickableSnapshot pickableSnapshot = value?.Pickable; bool flag = vneiEntries?.Any((PrefabConfigurationEntry entry) => entry.Pickable != null) ?? false; if (!flag && pickableSnapshot == null) { return false; } GameObject val = pickableSnapshot?.ItemPrefab; int num = Math.Max(1, pickableSnapshot?.Amount ?? 1); HashSet seen = new HashSet(StringComparer.Ordinal); IEnumerable enumerable = vneiEntries; foreach (PrefabConfigurationEntry item in enumerable ?? Enumerable.Empty()) { PickableDefinition pickable2 = item.Pickable; if (pickable2 == null || DropConditionEvaluator.HasConditions(item.Conditions) || !HasPickableDropOverride(pickable2.Drop)) { continue; } PickableDropDefinition drop = pickable2.Drop; if (!string.IsNullOrWhiteSpace(drop.Item)) { GameObject val2 = ResolveItemPrefab(drop.Item, prefabName + "/pickable.drop"); if ((Object)(object)val2 == (Object)null) { continue; } val = val2; } if (drop.Amount.HasValue) { num = Math.Max(1, drop.Amount.Value); } } if ((Object)(object)val != (Object)null) { AddUniqueVneiRow(results, seen, new VneiRecipeResult(((Object)val).name, 1, 1, 1f, num, num, 1f)); } enumerable = vneiEntries; foreach (PrefabConfigurationEntry item2 in enumerable ?? Enumerable.Empty()) { PickableDefinition pickable3 = item2.Pickable; if (pickable3 != null && DropConditionEvaluator.HasConditions(item2.Conditions) && HasPickableDropOverride(pickable3.Drop)) { PickableDropDefinition drop2 = pickable3.Drop; GameObject val3 = val; if (!string.IsNullOrWhiteSpace(drop2.Item)) { val3 = ResolveItemPrefab(drop2.Item, prefabName + "/pickable.drop"); } if (!((Object)(object)val3 == (Object)null)) { int num2 = (drop2.Amount.HasValue ? Math.Max(1, drop2.Amount.Value) : num); AddUniqueVneiRow(results, seen, new VneiRecipeResult(((Object)val3).name, 1, 1, 1f, num2, num2, 1f)); } } } if (TryGetVneiPickableExtraResults(prefab, value, vneiEntries, out List results2)) { foreach (VneiRecipeResult item3 in results2) { AddUniqueVneiRow(results, seen, item3); } } return flag || pickableSnapshot != null; } } internal static bool TryGetVneiDisplayForFish(Fish fish, out List results) { lock (Sync) { results = new List(); if ((Object)(object)fish == (Object)null || (Object)(object)((Component)fish).gameObject == (Object)null) { return false; } return TryGetVneiDropTableDisplay(((Component)fish).gameObject, (PrefabSnapshot snapshot) => snapshot.Fish?.ExtraDrops, (PrefabConfigurationEntry entry) => entry.Fish, (FishDefinition definition) => definition.ExtraDrops, "fish.extraDrops", 1, out results); } } internal static bool TryGetVneiDisplayForDestructible(Destructible destructible, out List results) { lock (Sync) { results = new List(); if ((Object)(object)destructible == (Object)null) { return false; } string prefabName = GetPrefabName(((Component)destructible).gameObject); List? vneiEntries = GetVneiEntries(prefabName); SnapshotState.SnapshotsByPrefab.TryGetValue(prefabName, out PrefabSnapshot value); GameObject val = value?.Destructible?.SpawnWhenDestroyed; bool flag = false; bool flag2 = false; HashSet seen = new HashSet(StringComparer.Ordinal); IEnumerable enumerable = vneiEntries; foreach (PrefabConfigurationEntry item in enumerable ?? Enumerable.Empty()) { DestructibleDefinition destructible2 = item.Destructible; if (destructible2 == null) { continue; } string text = (destructible2.SpawnWhenDestroyed ?? "").Trim(); if (text.Length == 0) { continue; } flag = true; GameObject val2 = ResolvePrefab(text); if (!((Object)(object)val2 == (Object)null)) { if (DropConditionEvaluator.HasConditions(item.Conditions)) { AddUniqueVneiRow(results, seen, new VneiRecipeResult(((Object)val2).name, 1, 1, 1f, 1, 1, 1f)); continue; } flag2 = true; val = val2; } } if ((Object)(object)val != (Object)null) { AddUniqueVneiRow(results, seen, new VneiRecipeResult(((Object)val).name, 1, 1, 1f, 1, 1, 1f)); } else if (!flag2 && (Object)(object)value?.Destructible?.SpawnWhenDestroyed != (Object)null) { AddUniqueVneiRow(results, seen, new VneiRecipeResult(((Object)value.Destructible.SpawnWhenDestroyed).name, 1, 1, 1f, 1, 1, 1f)); } return flag || (Object)(object)value?.Destructible?.SpawnWhenDestroyed != (Object)null; } } internal static bool TryGetVneiDisplayForTreeBase(TreeBase treeBase, out List results) { lock (Sync) { results = new List(); if ((Object)(object)treeBase == (Object)null) { return false; } bool flag = false; HashSet seen = new HashSet(StringComparer.Ordinal); if (TryGetVneiDropTableDisplay(((Component)treeBase).gameObject, (PrefabSnapshot snapshot) => snapshot.TreeBase, (PrefabConfigurationEntry entry) => entry.TreeBase, (DamageableDropTableDefinition payload) => payload, "treeBase", 1, out List results2)) { flag = true; foreach (VneiRecipeResult item in results2) { AddUniqueVneiRow(results, seen, item); } } if ((Object)(object)treeBase.m_stubPrefab != (Object)null && TryGetVneiDropTableDisplay(treeBase.m_stubPrefab, (PrefabSnapshot snapshot) => snapshot.DropOnDestroyed, (PrefabConfigurationEntry entry) => entry.DropOnDestroyed, (DropTableDefinition payload) => payload, "treeBase.stub", 1, out List results3)) { flag = true; foreach (VneiRecipeResult item2 in results3) { AddUniqueVneiRow(results, seen, item2); } } return flag | AppendTreeLogVneiResults(treeBase.m_logPrefab, 1, 0, results, seen); } } private static bool AppendTreeLogVneiResults(GameObject? logPrefab, int treeCount, int depth, List results, HashSet seen) { TreeLog val = default(TreeLog); if ((Object)(object)logPrefab == (Object)null || depth > 50 || !logPrefab.TryGetComponent(ref val)) { return false; } bool flag = false; if (val.m_subLogPoints != null && val.m_subLogPoints.Length != 0) { flag |= AppendTreeLogVneiResults(val.m_subLogPrefab, Math.Max(1, val.m_subLogPoints.Length), depth + 1, results, seen); } if (TryGetVneiDropTableDisplay(logPrefab, (PrefabSnapshot snapshot) => snapshot.TreeLog, (PrefabConfigurationEntry entry) => entry.TreeLog, (DamageableDropTableDefinition payload) => payload, "treeLog", Math.Max(1, treeCount), out List results2)) { flag = true; foreach (VneiRecipeResult item in results2) { AddUniqueVneiRow(results, seen, item); } } return flag; } private static bool TryGetVneiPickableExtraResults(GameObject prefab, PrefabSnapshot? snapshot, List? entries, out List results) { return TryGetVneiDropTableDisplay(prefab, (PrefabSnapshot _) => snapshot?.Pickable?.ExtraDrops, (PrefabConfigurationEntry entry) => entry.Pickable, (PickableDefinition definition) => definition.ExtraDrops, "pickable.extraDrops", 1, out results); } private static bool TryGetVneiDropTableDisplay(GameObject prefab, Func snapshotSelector, Func blockSelector, Func payloadSelector, string contextSuffix, int groupMultiplier, out List results) where TBlock : class { results = new List(); if ((Object)(object)prefab == (Object)null) { return false; } string prefabName = GetPrefabName(prefab); List? vneiEntries = GetVneiEntries(prefabName); SnapshotState.SnapshotsByPrefab.TryGetValue(prefabName, out PrefabSnapshot value); DropTable val = ((value != null) ? snapshotSelector(value) : null); bool flag = false; List list = new List(); List list2 = new List(); IEnumerable enumerable = vneiEntries; foreach (PrefabConfigurationEntry item in enumerable ?? Enumerable.Empty()) { TBlock val2 = blockSelector(item); if (val2 == null) { continue; } DropTablePayloadDefinition dropTablePayloadDefinition = payloadSelector(val2); if (HasDropTableOverride(dropTablePayloadDefinition)) { flag = true; if (DropConditionEvaluator.HasConditions(item.Conditions)) { list2.Add(dropTablePayloadDefinition); } else { list.Add(dropTablePayloadDefinition); } } } if (!flag && val == null) { return false; } DropTable val3 = ((list.Count > 0) ? BuildEffectiveDropTable(val, list, prefabName + "/" + contextSuffix) : ((val != null) ? CloneDropTable(val) : CreateDefaultDropTable())); List list3 = val3.m_drops.Select(CloneDropData).ToList(); HashSet seenFingerprints = new HashSet(list3.Select(BuildDropRowFingerprint), StringComparer.Ordinal); foreach (DropTablePayloadDefinition item2 in list2) { AppendDropTableRows(list3, item2.Drops, prefabName + "/" + contextSuffix, seenFingerprints); } results = BuildVneiResultsFromDropTable(val3, list3, groupMultiplier); if (!flag) { return val != null; } return true; } private static List BuildVneiResultsFromDropTable(DropTable table, List rows, int groupMultiplier) { //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: Unknown result type (might be due to invalid IL or missing references) //IL_00d2: Unknown result type (might be due to invalid IL or missing references) //IL_00e1: Unknown result type (might be due to invalid IL or missing references) List list = new List(); if (table == null || rows == null || rows.Count == 0) { return list; } float num = rows.Sum((DropData row) => Mathf.Max(0f, row.m_weight)); int num2 = Math.Max(0, table.m_dropMin * Math.Max(1, groupMultiplier)); int groupMax = Math.Max(num2, table.m_dropMax * Math.Max(1, groupMultiplier)); float groupChance = Mathf.Clamp01(table.m_dropChance); foreach (DropData row in rows) { if (!((Object)(object)row.m_item == (Object)null)) { float chance = ((num <= 0f) ? 1f : Mathf.Clamp01(Mathf.Max(0f, row.m_weight) / num)); int num3 = Math.Max(1, row.m_stackMin); int max = Math.Max(num3, row.m_stackMax); list.Add(new VneiRecipeResult(((Object)row.m_item).name, num2, groupMax, groupChance, num3, max, chance)); } } return list; } private static DropData CloneDropData(DropData row) { //IL_0002: 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_0017: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0031: 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_0049: Unknown result type (might be due to invalid IL or missing references) return new DropData { m_item = row.m_item, m_stackMin = row.m_stackMin, m_stackMax = row.m_stackMax, m_weight = row.m_weight, m_dontScale = row.m_dontScale }; } private static string BuildDropRowFingerprint(DropData row) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_002c: 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_004e: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) return BuildDropRowFingerprint(new DropEntryDefinition { Item = (((Object)(object)row.m_item != (Object)null) ? ((Object)row.m_item).name : ""), StackMin = row.m_stackMin, StackMax = row.m_stackMax, Weight = row.m_weight, DontScale = row.m_dontScale }); } private static void AddUniqueVneiRow(List results, HashSet seen, VneiRecipeResult row) { string item = string.Join("\n", row.PrefabName ?? "", row.GroupMin, row.GroupMax, row.GroupChance.ToString("R"), row.Min, row.Max, row.Chance.ToString("R")); if (seen.Add(item)) { results.Add(row); } } private static GameObject? ResolvePrefab(string prefabName) { 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; if (instance2 == null) { return null; } obj = instance2.GetItemPrefab(text); } return (GameObject?)obj; } private static bool HasRelevantVneiOverride(string prefabName, Func predicate) { return GetVneiEntries(prefabName ?? "")?.Any(predicate) ?? false; } } internal static class PrefabProvenanceRegistry { internal sealed class MappingSnapshot { internal Dictionary Owners { get; } internal string Signature { get; } internal MappingSnapshot(Dictionary owners, string signature) { Owners = owners ?? new Dictionary(StringComparer.OrdinalIgnoreCase); Signature = signature ?? ""; } } private enum ProviderKind { JotunnZoneManager, LocationManagerTemplate, CreatureManagerTemplate, JotunnCreatureManager, JotunnPrefabManager } private sealed class ProviderHandle { public ProviderKind Kind { get; set; } public Assembly Assembly { get; set; } public string AssemblyName { get; set; } = ""; public string OwnerName { get; set; } = ""; public Type? ManagerType { get; set; } public PropertyInfo? ManagerInstanceProperty { get; set; } public FieldInfo? ManagerCreaturesField { get; set; } public PropertyInfo? ManagerCreaturesProperty { get; set; } public FieldInfo? RegisteredCreaturesField { get; set; } public FieldInfo? RegisteredLocationsField { get; set; } public PropertyInfo? PrefabsProperty { get; set; } public FieldInfo? PrefabsField { get; set; } public PropertyInfo? LocationsProperty { get; set; } public FieldInfo? LocationsField { get; set; } } private readonly struct OwnerMapping { internal string OwnerName { get; } internal int Priority { get; } internal OwnerMapping(string ownerName, int priority) { OwnerName = ownerName; Priority = priority; } } private sealed class InternalMappingSnapshot { public Dictionary Owners { get; set; } = new Dictionary(StringComparer.OrdinalIgnoreCase); public string Signature { get; set; } = ""; } private static readonly object Sync = new object(); private static string _providerAssemblySignature = ""; private static List _providerCache = new List(); private static string _mappingSignature = ""; private static Dictionary _owners = new Dictionary(StringComparer.OrdinalIgnoreCase); private static MappingSnapshot _snapshot = new MappingSnapshot(new Dictionary(StringComparer.OrdinalIgnoreCase), ""); private static bool _mappingsDirty = true; private static bool _allowCacheLoad = true; internal static void Invalidate() { lock (Sync) { _mappingSignature = ""; _owners = new Dictionary(StringComparer.OrdinalIgnoreCase); _snapshot = new MappingSnapshot(new Dictionary(StringComparer.OrdinalIgnoreCase), ""); _mappingsDirty = true; _allowCacheLoad = false; } } internal static MappingSnapshot GetSnapshot() { EnsureMappingsLoaded(); return _snapshot; } private static void EnsureMappingsLoaded() { if (!_mappingsDirty && _snapshot.Signature.Length > 0) { return; } lock (Sync) { if (!_mappingsDirty && _snapshot.Signature.Length > 0) { return; } Assembly[] array = (from assembly in AppDomain.CurrentDomain.GetAssemblies() where !assembly.IsDynamic select assembly).ToArray(); string text = ReferenceRefreshSupport.ComputeStableHashForKeys(array.Select((Assembly assembly) => assembly.FullName ?? assembly.GetName().Name ?? "")); if (_allowCacheLoad && TryLoadMappingsSnapshotFromCache(text, out InternalMappingSnapshot snapshot)) { _providerAssemblySignature = text; _owners = snapshot.Owners; _mappingSignature = snapshot.Signature; _snapshot = new MappingSnapshot(_owners.ToDictionary, string, string>((KeyValuePair pair) => pair.Key, (KeyValuePair pair) => pair.Value.OwnerName, StringComparer.OrdinalIgnoreCase), _mappingSignature); _mappingsDirty = false; return; } InternalMappingSnapshot internalMappingSnapshot = BuildMappingsSnapshot(GetProviders(array, text)); if (!string.Equals(internalMappingSnapshot.Signature, _mappingSignature, StringComparison.Ordinal)) { _owners = internalMappingSnapshot.Owners; _mappingSignature = internalMappingSnapshot.Signature; _snapshot = new MappingSnapshot(_owners.ToDictionary, string, string>((KeyValuePair pair) => pair.Key, (KeyValuePair pair) => pair.Value.OwnerName, StringComparer.OrdinalIgnoreCase), _mappingSignature); } SaveMappingsSnapshotToCache(text, internalMappingSnapshot); _mappingsDirty = false; _allowCacheLoad = true; } } private static string GetCachePath() { string text = Path.Combine(DropNSpawnPlugin.YamlConfigDirectoryPath, "cache"); Directory.CreateDirectory(text); return Path.Combine(text, ".prefab-owner-provenance-cache.txt"); } private static bool TryLoadMappingsSnapshotFromCache(string assemblySignature, out InternalMappingSnapshot snapshot) { snapshot = new InternalMappingSnapshot(); string cachePath = GetCachePath(); if (!File.Exists(cachePath)) { return false; } try { string[] array = File.ReadAllLines(cachePath); if (array.Length < 3 || !string.Equals(array[0], "v1", StringComparison.Ordinal)) { return false; } if (!string.Equals(array[1], assemblySignature, StringComparison.Ordinal)) { return false; } Dictionary dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); for (int i = 3; i < array.Length; i++) { string text = array[i]; if (string.IsNullOrWhiteSpace(text)) { continue; } string[] array2 = text.Split('\t'); if (array2.Length >= 3) { string text2 = DecodeCacheField(array2[0]); string text3 = DecodeCacheField(array2[1]); if (int.TryParse(array2[2], out var result) && !string.IsNullOrWhiteSpace(text2) && !string.IsNullOrWhiteSpace(text3)) { dictionary[text2] = new OwnerMapping(text3, result); } } } snapshot = new InternalMappingSnapshot { Owners = dictionary, Signature = (array[2] ?? "") }; return snapshot.Signature.Length > 0; } catch { return false; } } private static void SaveMappingsSnapshotToCache(string assemblySignature, InternalMappingSnapshot snapshot) { if (string.IsNullOrWhiteSpace(assemblySignature) || string.IsNullOrWhiteSpace(snapshot.Signature)) { return; } StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine("v1"); stringBuilder.AppendLine(assemblySignature); stringBuilder.AppendLine(snapshot.Signature); foreach (var (value, ownerMapping2) in snapshot.Owners.OrderBy, string>((KeyValuePair pair) => pair.Key, StringComparer.OrdinalIgnoreCase)) { stringBuilder.Append(EncodeCacheField(value)).Append('\t').Append(EncodeCacheField(ownerMapping2.OwnerName)) .Append('\t') .Append(ownerMapping2.Priority) .AppendLine(); } GeneratedFileWriter.WriteAllTextIfChanged(GetCachePath(), stringBuilder.ToString()); } private static string EncodeCacheField(string? value) { return Convert.ToBase64String(Encoding.UTF8.GetBytes(value ?? "")); } private static string DecodeCacheField(string? value) { if (string.IsNullOrWhiteSpace(value)) { return ""; } try { return Encoding.UTF8.GetString(Convert.FromBase64String(value)); } catch { return ""; } } private static List GetProviders(Assembly[] assemblies, string assemblySignature) { if (string.Equals(_providerAssemblySignature, assemblySignature, StringComparison.Ordinal)) { return _providerCache; } List list = new List(); ProviderHandle providerHandle = TryCreateJotunnZoneProvider(assemblies); if (providerHandle != null) { list.Add(providerHandle); } ProviderHandle providerHandle2 = TryCreateJotunnPrefabProvider(assemblies); if (providerHandle2 != null) { list.Add(providerHandle2); } ProviderHandle providerHandle3 = TryCreateJotunnCreatureProvider(assemblies); if (providerHandle3 != null) { list.Add(providerHandle3); } list.AddRange(CreateCreatureManagerTemplateProviders(assemblies)); list.AddRange(CreateLocationManagerTemplateProviders(assemblies)); list = list.OrderBy((ProviderHandle provider) => provider.Kind).ThenBy((ProviderHandle provider) => provider.OwnerName, StringComparer.OrdinalIgnoreCase).ThenBy((ProviderHandle provider) => provider.AssemblyName, StringComparer.OrdinalIgnoreCase) .ToList(); _providerAssemblySignature = assemblySignature; _providerCache = list; return _providerCache; } private static ProviderHandle? TryCreateJotunnZoneProvider(IEnumerable assemblies) { foreach (Assembly assembly in assemblies) { Type type = SafeGetType(assembly, "Jotunn.Managers.ZoneManager"); if (!(type == null)) { PropertyInfo propertyInfo = TryGetTypeProperty(type, "Instance"); PropertyInfo propertyInfo2 = TryGetTypeProperty(type, "Locations"); FieldInfo fieldInfo = TryGetTypeField(type, "Locations"); if (!(propertyInfo == null) && (!(propertyInfo2 == null) || !(fieldInfo == null))) { return new ProviderHandle { Kind = ProviderKind.JotunnZoneManager, Assembly = assembly, AssemblyName = (assembly.GetName().Name ?? assembly.FullName ?? "Jotunn"), OwnerName = ResolveAssemblyOwnerName(assembly), ManagerType = type, ManagerInstanceProperty = propertyInfo, LocationsProperty = propertyInfo2, LocationsField = fieldInfo }; } } } return null; } private static ProviderHandle? TryCreateJotunnPrefabProvider(IEnumerable assemblies) { foreach (Assembly assembly in assemblies) { Type type = SafeGetType(assembly, "Jotunn.Managers.PrefabManager"); if (!(type == null)) { PropertyInfo propertyInfo = TryGetTypeProperty(type, "Instance"); PropertyInfo propertyInfo2 = TryGetTypeProperty(type, "Prefabs"); FieldInfo fieldInfo = TryGetTypeField(type, "Prefabs"); if (!(propertyInfo == null) && (!(propertyInfo2 == null) || !(fieldInfo == null))) { return new ProviderHandle { Kind = ProviderKind.JotunnPrefabManager, Assembly = assembly, AssemblyName = (assembly.GetName().Name ?? assembly.FullName ?? "Jotunn"), OwnerName = ResolveAssemblyOwnerName(assembly), ManagerType = type, ManagerInstanceProperty = propertyInfo, PrefabsProperty = propertyInfo2, PrefabsField = fieldInfo }; } } } return null; } private static ProviderHandle? TryCreateJotunnCreatureProvider(IEnumerable assemblies) { foreach (Assembly assembly in assemblies) { Type type = SafeGetType(assembly, "Jotunn.Managers.CreatureManager"); if (!(type == null)) { PropertyInfo propertyInfo = TryGetTypeProperty(type, "Instance"); FieldInfo fieldInfo = TryGetTypeField(type, "Creatures"); PropertyInfo propertyInfo2 = TryGetTypeProperty(type, "Creatures"); if (!(propertyInfo == null) && (!(fieldInfo == null) || !(propertyInfo2 == null))) { return new ProviderHandle { Kind = ProviderKind.JotunnCreatureManager, Assembly = assembly, AssemblyName = (assembly.GetName().Name ?? assembly.FullName ?? "Jotunn"), OwnerName = ResolveAssemblyOwnerName(assembly), ManagerType = type, ManagerInstanceProperty = propertyInfo, ManagerCreaturesField = fieldInfo, ManagerCreaturesProperty = propertyInfo2 }; } } } return null; } private static IEnumerable CreateCreatureManagerTemplateProviders(IEnumerable assemblies) { foreach (Assembly assembly in assemblies) { Type type = SafeGetType(assembly, "CreatureManager.Creature"); if (!(type == null)) { FieldInfo fieldInfo = TryGetTypeField(type, "registeredCreatures"); if (!(fieldInfo == null)) { yield return new ProviderHandle { Kind = ProviderKind.CreatureManagerTemplate, Assembly = assembly, AssemblyName = (assembly.GetName().Name ?? assembly.FullName ?? "Unknown / Untracked"), OwnerName = ResolveAssemblyOwnerName(assembly), RegisteredCreaturesField = fieldInfo }; } } } } private static IEnumerable CreateLocationManagerTemplateProviders(IEnumerable assemblies) { foreach (Assembly assembly in assemblies) { Type type = SafeGetType(assembly, "LocationManager.Location"); if (!(type == null)) { FieldInfo fieldInfo = TryGetTypeField(type, "registeredLocations"); if (!(fieldInfo == null)) { yield return new ProviderHandle { Kind = ProviderKind.LocationManagerTemplate, Assembly = assembly, AssemblyName = (assembly.GetName().Name ?? assembly.FullName ?? "Unknown / Untracked"), OwnerName = ResolveAssemblyOwnerName(assembly), RegisteredLocationsField = fieldInfo }; } } } } private static InternalMappingSnapshot BuildMappingsSnapshot(IEnumerable providers) { Dictionary owners = new Dictionary(StringComparer.OrdinalIgnoreCase); List list = new List(); foreach (ProviderHandle provider in providers) { switch (provider.Kind) { case ProviderKind.JotunnZoneManager: CollectJotunnLocationMappings(provider, owners, list); break; case ProviderKind.LocationManagerTemplate: CollectLocationManagerTemplateMappings(provider, owners, list); break; case ProviderKind.JotunnPrefabManager: CollectJotunnPrefabMappings(provider, owners, list); break; case ProviderKind.JotunnCreatureManager: CollectJotunnCreatureMappings(provider, owners, list); break; case ProviderKind.CreatureManagerTemplate: CollectCreatureManagerTemplateMappings(provider, owners, list); break; } } return new InternalMappingSnapshot { Owners = owners, Signature = ReferenceRefreshSupport.ComputeStableHashForKeys(list) }; } private static void CollectJotunnLocationMappings(ProviderHandle provider, Dictionary owners, List tokens) { object obj = provider.ManagerInstanceProperty?.GetValue(null, null); if (obj == null) { return; } object obj2 = provider.LocationsField?.GetValue(obj) ?? provider.LocationsProperty?.GetValue(obj, null); IEnumerable source; if (!(obj2 is IDictionary dictionary)) { source = GetEnumerable(obj2); } else { IEnumerable values = dictionary.Values; source = values; } foreach (object item in (from object entry in source where entry != null select (entry)).OrderBy((object entry) => GetPrefabNameFromHolder(entry) ?? "", StringComparer.OrdinalIgnoreCase)) { string prefabNameFromHolder = GetPrefabNameFromHolder(item); string text = ResolveOwnerNameFromSourceModHolder(item, provider.OwnerName); if (!string.IsNullOrWhiteSpace(prefabNameFromHolder) && !string.IsNullOrWhiteSpace(text)) { TryAddOwnerMapping(owners, prefabNameFromHolder, text, 0); tokens.Add("jl:" + prefabNameFromHolder + ":" + text); } } } private static void CollectJotunnPrefabMappings(ProviderHandle provider, Dictionary owners, List tokens) { object obj = provider.ManagerInstanceProperty?.GetValue(null, null); if (obj == null) { return; } object obj2 = provider.PrefabsField?.GetValue(obj) ?? provider.PrefabsProperty?.GetValue(obj, null); IEnumerable source; if (!(obj2 is IDictionary dictionary)) { source = GetEnumerable(obj2); } else { IEnumerable values = dictionary.Values; source = values; } foreach (object item in (from object entry in source where entry != null select (entry)).OrderBy((object entry) => GetPrefabNameFromHolder(entry) ?? "", StringComparer.OrdinalIgnoreCase)) { string prefabNameFromHolder = GetPrefabNameFromHolder(item); string text = ResolveOwnerNameFromSourceModHolder(item, provider.OwnerName); if (!string.IsNullOrWhiteSpace(prefabNameFromHolder) && !string.IsNullOrWhiteSpace(text)) { TryAddOwnerMapping(owners, prefabNameFromHolder, text, 2); tokens.Add("jp:" + prefabNameFromHolder + ":" + text); } } } private static void CollectJotunnCreatureMappings(ProviderHandle provider, Dictionary owners, List tokens) { object obj = provider.ManagerInstanceProperty?.GetValue(null, null); if (obj == null) { return; } foreach (object item in (from object entry in GetEnumerable(provider.ManagerCreaturesField?.GetValue(obj) ?? provider.ManagerCreaturesProperty?.GetValue(obj, null)) where entry != null select (entry)).OrderBy((object entry) => GetPrefabNameFromHolder(entry) ?? "", StringComparer.OrdinalIgnoreCase)) { string prefabNameFromHolder = GetPrefabNameFromHolder(item); string text = ResolveOwnerNameFromSourceModHolder(item, provider.OwnerName); if (!string.IsNullOrWhiteSpace(prefabNameFromHolder) && !string.IsNullOrWhiteSpace(text)) { TryAddOwnerMapping(owners, prefabNameFromHolder, text, 0); tokens.Add("jc:" + prefabNameFromHolder + ":" + text); } } } private static void CollectCreatureManagerTemplateMappings(ProviderHandle provider, Dictionary owners, List tokens) { foreach (object item in (from object entry in GetEnumerable(provider.RegisteredCreaturesField?.GetValue(null)) where entry != null select entry).OrderBy((object entry) => GetPrefabNameFromHolder(entry) ?? "", StringComparer.OrdinalIgnoreCase)) { string prefabNameFromHolder = GetPrefabNameFromHolder(item); if (!string.IsNullOrWhiteSpace(prefabNameFromHolder) && !string.IsNullOrWhiteSpace(provider.OwnerName)) { TryAddOwnerMapping(owners, prefabNameFromHolder, provider.OwnerName, 1); tokens.Add("cm:" + provider.OwnerName + ":" + prefabNameFromHolder); } } } private static void CollectLocationManagerTemplateMappings(ProviderHandle provider, Dictionary owners, List tokens) { foreach (object item in (from object entry in GetEnumerable(provider.RegisteredLocationsField?.GetValue(null)) where entry != null select entry).OrderBy((object entry) => GetPrefabNameFromHolder(entry) ?? "", StringComparer.OrdinalIgnoreCase)) { string prefabNameFromHolder = GetPrefabNameFromHolder(item); if (!string.IsNullOrWhiteSpace(prefabNameFromHolder) && !string.IsNullOrWhiteSpace(provider.OwnerName)) { TryAddOwnerMapping(owners, prefabNameFromHolder, provider.OwnerName, 1); tokens.Add("lm:" + provider.OwnerName + ":" + prefabNameFromHolder); } } } private static void TryAddOwnerMapping(Dictionary owners, string prefabName, string ownerName, int priority) { string text = (prefabName ?? "").Trim(); string text2 = (ownerName ?? "").Trim(); if (text.Length != 0 && text2.Length != 0 && (!owners.TryGetValue(text, out var value) || value.Priority > priority)) { owners[text] = new OwnerMapping(text2, priority); } } private static string? GetPrefabNameFromHolder(object? holder) { if (holder == null) { return null; } if (TryGetRawMemberValue(holder, "Prefab", out object value)) { GameObject val = (GameObject)((value is GameObject) ? value : null); if (val != null) { string text = (((Object)val).name ?? "").Trim(); if (text.Length > 0) { return text; } return GetPrefabNameFromLocationHolder(holder); } } return GetPrefabNameFromLocationHolder(holder); } private static string? GetPrefabNameFromLocationHolder(object holder) { if (TryGetLocationComponentHolderPrefabName(holder, "Location", out string prefabName) || TryGetLocationComponentHolderPrefabName(holder, "location", out prefabName)) { return prefabName; } if (TryGetRawMemberValue(holder, "ZoneLocation", out object value) && value != null && TryGetRawMemberValue(value, "m_prefabName", out object value2)) { string text = (value2?.ToString() ?? "").Trim(); if (text.Length > 0) { return text; } } return null; } private static bool TryGetLocationComponentHolderPrefabName(object holder, string memberName, out string? prefabName) { prefabName = null; if (!TryGetRawMemberValue(holder, memberName, out object value) || value == null) { return false; } GameObject val = (GameObject)((value is GameObject) ? value : null); if (val != null) { string text = (((Object)val).name ?? "").Trim(); if (text.Length == 0) { return false; } prefabName = text; return true; } Location val2 = (Location)((value is Location) ? value : null); if (val2 != null) { string text2 = (((Object)val2).name ?? "").Trim(); if (text2.Length == 0) { return false; } prefabName = text2; return true; } return false; } private static string ResolveOwnerNameFromSourceModHolder(object holder, string fallbackOwnerName) { if (TryGetRawMemberValue(holder, "SourceMod", out object value) && value != null && TryResolvePluginOwnerName(value, out string ownerName)) { return ownerName; } return fallbackOwnerName; } private static string ResolveAssemblyOwnerName(Assembly assembly) { foreach (PluginInfo value in Chainloader.PluginInfos.Values) { if ((object)((object)value.Instance)?.GetType().Assembly == assembly) { string text = (value.Metadata.Name ?? "").Trim(); if (text.Length > 0) { return text; } string text2 = (value.Metadata.GUID ?? "").Trim(); if (text2.Length > 0) { return text2; } } } string text3 = assembly.GetName().Name ?? assembly.FullName ?? ""; if (!string.IsNullOrWhiteSpace(text3)) { return text3; } return "Unknown / Untracked"; } private static bool TryResolvePluginOwnerName(object sourceMod, out string ownerName) { ownerName = ""; if (TryGetRawMemberValue(sourceMod, "GUID", out object value)) { string text = (value?.ToString() ?? "").Trim(); if (text.Length > 0 && Chainloader.PluginInfos.TryGetValue(text, out var value2)) { string text2 = (value2.Metadata.Name ?? "").Trim(); ownerName = ((text2.Length > 0) ? text2 : text); return true; } if (text.Length > 0) { ownerName = text; return true; } } if (TryGetRawMemberValue(sourceMod, "Name", out object value3)) { string text3 = (value3?.ToString() ?? "").Trim(); if (text3.Length > 0) { ownerName = text3; return true; } } return false; } private static IEnumerable GetEnumerable(object? value) { return (value as IEnumerable) ?? Array.Empty(); } private static bool TryGetRawMemberValue(object instance, string memberName, out object? value) { value = null; Type type = instance.GetType(); while (type != null) { PropertyInfo property = type.GetProperty(memberName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (property != null) { try { value = property.GetValue(instance, null); return true; } catch { return false; } } FieldInfo field = type.GetField(memberName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (field != null) { try { value = field.GetValue(instance); return true; } catch { return false; } } type = type.BaseType; } return false; } private static Type? SafeGetType(Assembly assembly, string fullTypeName) { try { return assembly.GetType(fullTypeName, throwOnError: false, ignoreCase: false); } catch { return null; } } private static PropertyInfo? TryGetTypeProperty(Type? type, string memberName) { if (type == null || string.IsNullOrWhiteSpace(memberName)) { return null; } Type type2 = type; while (type2 != null) { PropertyInfo propertyInfo = type2.GetProperty(memberName, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic) ?? type2.GetProperties(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault((PropertyInfo candidate) => string.Equals(candidate.Name, memberName, StringComparison.OrdinalIgnoreCase)); if (propertyInfo != null) { return propertyInfo; } type2 = type2.BaseType; } return null; } private static FieldInfo? TryGetTypeField(Type? type, string memberName) { if (type == null || string.IsNullOrWhiteSpace(memberName)) { return null; } Type type2 = type; while (type2 != null) { FieldInfo fieldInfo = type2.GetField(memberName, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic) ?? type2.GetFields(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault((FieldInfo candidate) => string.Equals(candidate.Name, memberName, StringComparison.OrdinalIgnoreCase)); if (fieldInfo != null) { return fieldInfo; } type2 = type2.BaseType; } return null; } private static IEnumerable EnumerateLookupCandidates(string normalizedPrefabName) { HashSet seen = new HashSet(StringComparer.OrdinalIgnoreCase); YieldIfNew(normalizedPrefabName); string text = TrimCloneSuffix(normalizedPrefabName); YieldIfNew(text); int num = text.IndexOf(':'); if (num > 0) { YieldIfNew(text.Substring(0, num)); } foreach (string item in seen) { yield return item; } void YieldIfNew(string candidate) { if (!string.IsNullOrWhiteSpace(candidate)) { seen.Add(candidate.Trim()); } } } private static string TrimCloneSuffix(string prefabName) { if (!prefabName.EndsWith("(Clone)", StringComparison.Ordinal)) { return prefabName; } int length = "(Clone)".Length; return prefabName.Substring(0, prefabName.Length - length).TrimEnd(); } } internal sealed class PrefabOwnerSection { internal string OwnerName { get; } internal List Entries { get; } internal PrefabOwnerSection(string ownerName, List entries) { OwnerName = (string.IsNullOrWhiteSpace(ownerName) ? "Unknown / Untracked" : ownerName.Trim()); Entries = entries ?? new List(); } } internal static class PrefabOutputSections { private sealed class GroupedEntry { public T Entry { get; set; } public string PrefabName { get; set; } = ""; public string OwnerName { get; set; } = "Unknown / Untracked"; } internal static List> BuildSections(IEnumerable entries, Func getPrefabName) { PrefabOwnerResolver.OwnerSnapshot snapshot = PrefabOwnerResolver.GetSnapshot(); return BuildSections(entries, getPrefabName, (T entry) => snapshot.GetOwnerName(getPrefabName(entry))); } internal static List> BuildSections(IEnumerable entries, Func getPrefabName, Func getOwnerName) { return (from @group in (from entry in entries.Select(delegate(T entry) { string prefabName = (getPrefabName(entry) ?? "").Trim(); string text = (getOwnerName(entry) ?? "").Trim(); return new GroupedEntry { Entry = entry, PrefabName = prefabName, OwnerName = ((text.Length > 0) ? text : "Unknown / Untracked") }; }) orderby GetOwnerSortBucket(entry.OwnerName) select entry).ThenBy, string>((GroupedEntry entry) => entry.OwnerName, StringComparer.OrdinalIgnoreCase).ThenBy, string>((GroupedEntry entry) => entry.PrefabName, StringComparer.OrdinalIgnoreCase).GroupBy, string>((GroupedEntry entry) => entry.OwnerName, StringComparer.OrdinalIgnoreCase) select new PrefabOwnerSection(@group.First().OwnerName, @group.Select((GroupedEntry entry) => entry.Entry).ToList())).ToList(); } internal static string SerializeReferenceSections(IEnumerable> sections, ISerializer serializer) { StringBuilder stringBuilder = new StringBuilder(); bool flag = false; foreach (PrefabOwnerSection section in sections) { if (section.Entries.Count == 0) { continue; } if (flag) { stringBuilder.AppendLine(); } AppendSectionHeaderComment(stringBuilder, section.OwnerName); foreach (T entry in section.Entries) { string value = CollapseScalarBlockListsToInlineLists(serializer.Serialize(new T[1] { entry }).TrimEnd('\r', '\n')); stringBuilder.AppendLine(value); } flag = true; } if (!flag) { return "[]" + Environment.NewLine; } return stringBuilder.ToString(); } internal static void AppendSectionHeaderComment(StringBuilder builder, string ownerName) { builder.Append("# ===== "); builder.Append(string.IsNullOrWhiteSpace(ownerName) ? "Unknown / Untracked" : ownerName.Trim()); builder.AppendLine(" ====="); } private static string CollapseScalarBlockListsToInlineLists(string yaml) { if (string.IsNullOrWhiteSpace(yaml) || yaml.IndexOf("- ", StringComparison.Ordinal) < 0) { return yaml; } string[] array = yaml.Replace("\r\n", "\n").Split('\n'); StringBuilder stringBuilder = new StringBuilder(); for (int i = 0; i < array.Length; i++) { if (TryCollapseScalarBlockList(array, ref i, out string collapsedLine)) { stringBuilder.AppendLine(collapsedLine); } else { stringBuilder.AppendLine(array[i]); } } return stringBuilder.ToString().TrimEnd('\r', '\n'); } private static bool TryCollapseScalarBlockList(string[] lines, ref int index, out string collapsedLine) { collapsedLine = ""; string text = lines[index]; if (string.IsNullOrWhiteSpace(text)) { return false; } int firstNonWhitespaceIndex = GetFirstNonWhitespaceIndex(text); if (firstNonWhitespaceIndex < 0) { return false; } string text2 = text; int num = firstNonWhitespaceIndex; if (text2.Substring(num, text2.Length - num).StartsWith("-", StringComparison.Ordinal)) { return false; } int num2 = text.LastIndexOf(':'); if (num2 < 0 || num2 != text.Length - 1) { return false; } string text3 = text.Substring(0, firstNonWhitespaceIndex) + "- "; List list = new List(); int i; for (i = index + 1; i < lines.Length && lines[i].StartsWith(text3, StringComparison.Ordinal); i++) { text2 = lines[i]; num = text3.Length; string text4 = text2.Substring(num, text2.Length - num); if (!IsSimpleScalarYamlListItem(text4)) { return false; } if (i + 1 < lines.Length) { string text5 = lines[i + 1]; if (GetFirstNonWhitespaceIndex(text5) > firstNonWhitespaceIndex && !text5.StartsWith(text3, StringComparison.Ordinal)) { return false; } } list.Add(text4); } if (list.Count == 0) { return false; } collapsedLine = text + " [" + string.Join(", ", list) + "]"; index = i - 1; return true; } private static bool IsSimpleScalarYamlListItem(string itemValue) { if (string.IsNullOrWhiteSpace(itemValue)) { return false; } string text = itemValue.Trim(); if (text.EndsWith(":", StringComparison.Ordinal) || text.Contains(": ", StringComparison.Ordinal)) { return false; } return true; } private static int GetFirstNonWhitespaceIndex(string line) { for (int i = 0; i < line.Length; i++) { if (!char.IsWhiteSpace(line[i])) { return i; } } return -1; } private static int GetOwnerSortBucket(string ownerName) { if (string.Equals(ownerName, "Valheim", StringComparison.OrdinalIgnoreCase)) { return 0; } if (string.Equals(ownerName, "Unknown / Untracked", StringComparison.OrdinalIgnoreCase)) { return 2; } return 1; } } internal static class PrefabOwnerResolver { internal sealed class OwnerSnapshot { private readonly Dictionary _owners; internal string Signature { get; } internal OwnerSnapshot(Dictionary owners, string signature) { _owners = owners ?? new Dictionary(StringComparer.OrdinalIgnoreCase); Signature = signature ?? ""; } internal string GetOwnerName(string? prefabName) { string text = (prefabName ?? "").Trim(); if (text.Length == 0) { return "Unknown / Untracked"; } foreach (string item in EnumerateLookupCandidates(text)) { if (_owners.TryGetValue(item, out string value) && !string.IsNullOrWhiteSpace(value)) { return value; } } foreach (string item2 in EnumerateLookupCandidates(text)) { if (VanillaPrefabCatalog.IsAvailable && VanillaPrefabCatalog.IsVanilla(item2)) { return "Valheim"; } } foreach (string item3 in EnumerateLookupCandidates(text)) { if (PrefabOwnerCatalog.IsLikelyRuntimeVanillaPrefab(item3)) { return "Valheim"; } } return "Unknown / Untracked"; } private static IEnumerable EnumerateLookupCandidates(string normalizedPrefabName) { HashSet seen = new HashSet(StringComparer.OrdinalIgnoreCase); AddIfNew(normalizedPrefabName); string text = TrimCloneSuffix(normalizedPrefabName); AddIfNew(text); int num = text.IndexOf(':'); if (num > 0) { AddIfNew(text.Substring(0, num)); } foreach (string item in seen) { yield return item; } void AddIfNew(string candidate) { if (!string.IsNullOrWhiteSpace(candidate)) { seen.Add(candidate.Trim()); } } } private static string TrimCloneSuffix(string prefabName) { if (!prefabName.EndsWith("(Clone)", StringComparison.Ordinal)) { return prefabName; } int length = "(Clone)".Length; return prefabName.Substring(0, prefabName.Length - length).TrimEnd(); } } private static readonly object Sync = new object(); private static string _snapshotSignature = ""; private static OwnerSnapshot _snapshot = new OwnerSnapshot(new Dictionary(StringComparer.OrdinalIgnoreCase), ""); internal static void Invalidate() { lock (Sync) { _snapshotSignature = ""; _snapshot = new OwnerSnapshot(new Dictionary(StringComparer.OrdinalIgnoreCase), ""); } PrefabProvenanceRegistry.Invalidate(); } internal static string GetOwnerName(string? prefabName) { return GetSnapshot().GetOwnerName(prefabName); } internal static OwnerSnapshot GetSnapshot() { PrefabProvenanceRegistry.MappingSnapshot snapshot = PrefabProvenanceRegistry.GetSnapshot(); PrefabOwnerCatalog.MappingSnapshot snapshot2 = PrefabOwnerCatalog.GetSnapshot(); string text = "provenance:" + snapshot.Signature + "|bundles:" + snapshot2.Signature; if (string.Equals(text, _snapshotSignature, StringComparison.Ordinal)) { return _snapshot; } lock (Sync) { if (string.Equals(text, _snapshotSignature, StringComparison.Ordinal)) { return _snapshot; } Dictionary dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); string value; string key; foreach (KeyValuePair owner in snapshot2.Owners) { owner.Deconstruct(out value, out key); string text2 = value; string value2 = key; if (!string.IsNullOrWhiteSpace(text2) && !string.IsNullOrWhiteSpace(value2)) { dictionary[text2] = value2; } } foreach (KeyValuePair owner2 in snapshot.Owners) { owner2.Deconstruct(out key, out value); string text3 = key; string value3 = value; if (!string.IsNullOrWhiteSpace(text3) && !string.IsNullOrWhiteSpace(value3)) { dictionary[text3] = value3; } } _snapshot = new OwnerSnapshot(dictionary, text); _snapshotSignature = text; return _snapshot; } } } internal static class PrefabOwnerCatalog { internal sealed class MappingSnapshot { internal Dictionary Owners { get; } internal string Signature { get; } internal MappingSnapshot(Dictionary owners, string signature) { Owners = owners ?? new Dictionary(StringComparer.OrdinalIgnoreCase); Signature = signature ?? ""; } } private sealed class PluginResources { public string OwnerName { get; set; } = ""; public string[] ResourceNames { get; set; } = Array.Empty(); } internal const string VanillaOwnerName = "Valheim"; internal const string UnknownOwnerName = "Unknown / Untracked"; private static readonly object Sync = new object(); private static readonly Dictionary PrefabOwners = new Dictionary(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary BundleOwners = new Dictionary(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary> PrefabsByBundle = new Dictionary>(StringComparer.OrdinalIgnoreCase); private static readonly HashSet KnownBundleNames = new HashSet(StringComparer.OrdinalIgnoreCase); private static string _loadedBundleSignature = ""; private static string _loadedPluginResourcesSignature = ""; private static MappingSnapshot _snapshot = new MappingSnapshot(new Dictionary(StringComparer.OrdinalIgnoreCase), ""); private static string _pluginResourcesSignature = ""; private static List _pluginResourcesCache = new List(); private static bool _mappingsDirty = true; internal static string GetOwnerName(string? prefabName) { string text = (prefabName ?? "").Trim(); if (text.Length == 0) { return "Unknown / Untracked"; } foreach (string item in EnumerateLookupCandidates(text)) { if (VanillaPrefabCatalog.IsAvailable && VanillaPrefabCatalog.IsVanilla(item)) { return "Valheim"; } } return PrefabOwnerResolver.GetOwnerName(text); } internal static MappingSnapshot GetSnapshot() { EnsureMappingsLoaded(); return _snapshot; } internal static void Invalidate() { lock (Sync) { _mappingsDirty = true; } } private static void EnsureMappingsLoaded() { string text = BuildBundleSignature(); string text2 = BuildPluginResourcesSignature(); if (!_mappingsDirty && string.Equals(text, _loadedBundleSignature, StringComparison.Ordinal) && string.Equals(text2, _loadedPluginResourcesSignature, StringComparison.Ordinal)) { return; } lock (Sync) { if (!_mappingsDirty && string.Equals(text, _loadedBundleSignature, StringComparison.Ordinal) && string.Equals(text2, _loadedPluginResourcesSignature, StringComparison.Ordinal)) { return; } if (TryLoadMappingsFromCache(text, text2)) { _mappingsDirty = false; return; } Dictionary loadedBundlesByName = GetLoadedBundlesByName(); HashSet currentBundleNames = new HashSet(loadedBundlesByName.Keys, StringComparer.OrdinalIgnoreCase); if (TryIncrementallyExtendMappings(loadedBundlesByName, currentBundleNames, text, text2)) { _mappingsDirty = false; return; } RebuildMappings(loadedBundlesByName, text, text2); _mappingsDirty = false; } } private static void RebuildMappings(Dictionary loadedBundles, string bundleSignature, string pluginResourcesSignature) { PrefabOwners.Clear(); PrefabsByBundle.Clear(); KnownBundleNames.Clear(); Dictionary dictionary = BuildPrefabToBundleMapping(loadedBundles.Values); Dictionary dictionary2 = BuildBundleToOwnerMapping(dictionary.Values.Distinct(StringComparer.OrdinalIgnoreCase)); foreach (var (text3, key) in dictionary) { if (dictionary2.TryGetValue(key, out var value)) { PrefabOwners[text3] = value; } if (!PrefabsByBundle.TryGetValue(key, out HashSet value2)) { value2 = new HashSet(StringComparer.OrdinalIgnoreCase); PrefabsByBundle[key] = value2; } value2.Add(text3); } foreach (string key2 in loadedBundles.Keys) { KnownBundleNames.Add(key2); } _loadedBundleSignature = bundleSignature; _loadedPluginResourcesSignature = pluginResourcesSignature; RefreshSnapshot(); SaveMappingsToCache(); } private static Dictionary GetLoadedBundlesByName() { Dictionary dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); foreach (AssetBundle allLoadedAssetBundle in AssetBundle.GetAllLoadedAssetBundles()) { string text = ((Object)allLoadedAssetBundle).name ?? ""; if (text.Length != 0) { dictionary[text] = allLoadedAssetBundle; } } return dictionary; } private static Dictionary BuildPrefabToBundleMapping(IEnumerable bundles) { Dictionary dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); foreach (AssetBundle bundle in bundles) { string text = ((Object)bundle).name ?? ""; if (text.Length == 0) { continue; } string[] allAssetNames = bundle.GetAllAssetNames(); foreach (string text2 in allAssetNames) { if (text2.EndsWith(".prefab", StringComparison.OrdinalIgnoreCase)) { string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(text2); if (!string.IsNullOrWhiteSpace(fileNameWithoutExtension)) { dictionary[fileNameWithoutExtension] = text; } } } } return dictionary; } private static bool TryIncrementallyExtendMappings(Dictionary loadedBundles, HashSet currentBundleNames, string bundleSignature, string pluginResourcesSignature) { if (!string.Equals(_loadedPluginResourcesSignature, pluginResourcesSignature, StringComparison.Ordinal) || KnownBundleNames.Count == 0 || currentBundleNames.Count < KnownBundleNames.Count || KnownBundleNames.Except(currentBundleNames, StringComparer.OrdinalIgnoreCase).Any()) { return false; } List list = currentBundleNames.Except(KnownBundleNames, StringComparer.OrdinalIgnoreCase).OrderBy((string name) => name, StringComparer.OrdinalIgnoreCase).ToList(); if (list.Count == 0) { _loadedBundleSignature = bundleSignature; _loadedPluginResourcesSignature = pluginResourcesSignature; RefreshSnapshot(); return true; } Dictionary dictionary = BuildBundleToOwnerMapping(list); foreach (string item in list) { if (!loadedBundles.TryGetValue(item, out AssetBundle value)) { continue; } KnownBundleNames.Add(item); if (!PrefabsByBundle.TryGetValue(item, out HashSet value2)) { value2 = new HashSet(StringComparer.OrdinalIgnoreCase); PrefabsByBundle[item] = value2; } string value4; string value3 = (dictionary.TryGetValue(item, out value4) ? value4 : ""); string[] allAssetNames = value.GetAllAssetNames(); foreach (string text in allAssetNames) { if (!text.EndsWith(".prefab", StringComparison.OrdinalIgnoreCase)) { continue; } string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(text); if (!string.IsNullOrWhiteSpace(fileNameWithoutExtension)) { value2.Add(fileNameWithoutExtension); if (!string.IsNullOrWhiteSpace(value3)) { PrefabOwners[fileNameWithoutExtension] = value3; } } } } _loadedBundleSignature = bundleSignature; _loadedPluginResourcesSignature = pluginResourcesSignature; RefreshSnapshot(); SaveMappingsToCache(); return true; } private static Dictionary BuildBundleToOwnerMapping(IEnumerable bundleNames) { Dictionary dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); List pluginResources = GetPluginResources(); foreach (string item in bundleNames.Distinct(StringComparer.OrdinalIgnoreCase)) { if (item.Length == 0) { continue; } if (BundleOwners.TryGetValue(item, out string value)) { if (!string.IsNullOrWhiteSpace(value)) { dictionary[item] = value; } continue; } string text = ResolveOwnerName(item, pluginResources); BundleOwners[item] = text ?? ""; if (!string.IsNullOrWhiteSpace(text)) { dictionary[item] = text; } } return dictionary; } private static List GetPluginResources() { string text = BuildPluginResourcesSignature(); if (string.Equals(text, _pluginResourcesSignature, StringComparison.Ordinal)) { return _pluginResourcesCache; } List pluginResourcesCache = (from pluginInfo in Chainloader.PluginInfos.Values select new PluginResources { OwnerName = GetPluginDisplayName(pluginInfo), ResourceNames = GetManifestResourceNames(pluginInfo) } into plugin where plugin.OwnerName.Length > 0 select plugin).ToList(); _pluginResourcesSignature = text; _pluginResourcesCache = pluginResourcesCache; BundleOwners.Clear(); return _pluginResourcesCache; } private static void RefreshSnapshot() { _snapshot = new MappingSnapshot(new Dictionary(PrefabOwners, StringComparer.OrdinalIgnoreCase), ReferenceRefreshSupport.ComputeStableHashForKeys(new string[2] { "bundles:" + _loadedBundleSignature, "plugins:" + _loadedPluginResourcesSignature })); } private static string GetCachePath() { string text = Path.Combine(DropNSpawnPlugin.YamlConfigDirectoryPath, "cache"); Directory.CreateDirectory(text); return Path.Combine(text, ".prefab-owner-bundle-cache.txt"); } private static bool TryLoadMappingsFromCache(string bundleSignature, string pluginResourcesSignature) { string cachePath = GetCachePath(); if (!File.Exists(cachePath)) { return false; } try { string[] array = File.ReadAllLines(cachePath); if (array.Length < 4 || !string.Equals(array[0], "v1", StringComparison.Ordinal)) { return false; } if (!string.Equals(array[1], bundleSignature, StringComparison.Ordinal) || !string.Equals(array[2], pluginResourcesSignature, StringComparison.Ordinal)) { return false; } PrefabOwners.Clear(); PrefabsByBundle.Clear(); KnownBundleNames.Clear(); BundleOwners.Clear(); for (int i = 4; i < array.Length; i++) { string text = array[i]; if (string.IsNullOrWhiteSpace(text)) { continue; } string[] array2 = text.Split('\t'); if (array2.Length < 3) { continue; } string text2 = DecodeCacheField(array2[0]); string value = DecodeCacheField(array2[1]); string text3 = DecodeCacheField(array2[2]); if (!string.IsNullOrWhiteSpace(text2) && !string.IsNullOrWhiteSpace(text3)) { PrefabOwners[text2] = value; KnownBundleNames.Add(text3); if (!PrefabsByBundle.TryGetValue(text3, out HashSet value2)) { value2 = new HashSet(StringComparer.OrdinalIgnoreCase); PrefabsByBundle[text3] = value2; } value2.Add(text2); if (!string.IsNullOrWhiteSpace(value)) { BundleOwners[text3] = value; } } } _loadedBundleSignature = bundleSignature; _loadedPluginResourcesSignature = pluginResourcesSignature; _snapshot = new MappingSnapshot(new Dictionary(PrefabOwners, StringComparer.OrdinalIgnoreCase), array[3] ?? ""); return _snapshot.Signature.Length > 0; } catch { return false; } } private static void SaveMappingsToCache() { if (string.IsNullOrWhiteSpace(_loadedBundleSignature) || string.IsNullOrWhiteSpace(_loadedPluginResourcesSignature) || string.IsNullOrWhiteSpace(_snapshot.Signature)) { return; } StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine("v1"); stringBuilder.AppendLine(_loadedBundleSignature); stringBuilder.AppendLine(_loadedPluginResourcesSignature); stringBuilder.AppendLine(_snapshot.Signature); foreach (var (value, source) in PrefabsByBundle.OrderBy>, string>((KeyValuePair> pair) => pair.Key, StringComparer.OrdinalIgnoreCase)) { foreach (string item in source.OrderBy((string prefabName) => prefabName, StringComparer.OrdinalIgnoreCase)) { PrefabOwners.TryGetValue(item, out string value2); stringBuilder.Append(EncodeCacheField(item)).Append('\t').Append(EncodeCacheField(value2 ?? "")) .Append('\t') .Append(EncodeCacheField(value)) .AppendLine(); } } GeneratedFileWriter.WriteAllTextIfChanged(GetCachePath(), stringBuilder.ToString()); } private static string EncodeCacheField(string? value) { return Convert.ToBase64String(Encoding.UTF8.GetBytes(value ?? "")); } private static string DecodeCacheField(string? value) { if (string.IsNullOrWhiteSpace(value)) { return ""; } try { return Encoding.UTF8.GetString(Convert.FromBase64String(value)); } catch { return ""; } } private static string? ResolveOwnerName(string bundleName, List plugins) { return plugins.FirstOrDefault((PluginResources candidate) => candidate.ResourceNames.Any((string resourceName) => resourceName.EndsWith(bundleName, StringComparison.OrdinalIgnoreCase)))?.OwnerName; } private static string GetPluginDisplayName(PluginInfo pluginInfo) { string text = pluginInfo.Metadata.Name?.Trim() ?? ""; if (text.Length > 0) { return text; } string text2 = pluginInfo.Metadata.GUID?.Trim() ?? ""; if (text2.Length <= 0) { return "Unknown / Untracked"; } return text2; } private static string[] GetManifestResourceNames(PluginInfo pluginInfo) { try { return ((object)pluginInfo.Instance)?.GetType().Assembly.GetManifestResourceNames() ?? Array.Empty(); } catch { return Array.Empty(); } } private static string BuildPluginResourcesSignature() { return string.Join("|", Chainloader.PluginInfos.Values.Select(delegate(PluginInfo pluginInfo) { string text = pluginInfo.Metadata.GUID ?? ""; string text2 = pluginInfo.Metadata.Name ?? ""; string text3 = ((object)pluginInfo.Instance)?.GetType().Assembly.FullName ?? ""; return text + ":" + text2 + ":" + text3; }).OrderBy((string signature) => signature, StringComparer.OrdinalIgnoreCase)); } private static string BuildBundleSignature() { return string.Join("|", (from assetBundle in AssetBundle.GetAllLoadedAssetBundles() select ((Object)assetBundle).name ?? "").OrderBy((string name) => name, StringComparer.OrdinalIgnoreCase)); } private static IEnumerable EnumerateLookupCandidates(string normalizedPrefabName) { HashSet seen = new HashSet(StringComparer.OrdinalIgnoreCase); YieldIfNew(normalizedPrefabName); string text = TrimCloneSuffix(normalizedPrefabName); YieldIfNew(text); int num = text.IndexOf(':'); if (num > 0) { YieldIfNew(text.Substring(0, num)); } foreach (string item in seen) { yield return item; } void YieldIfNew(string candidate) { if (!string.IsNullOrWhiteSpace(candidate)) { seen.Add(candidate.Trim()); } } } private static string TrimCloneSuffix(string prefabName) { if (!prefabName.EndsWith("(Clone)", StringComparison.Ordinal)) { return prefabName; } int length = "(Clone)".Length; return prefabName.Substring(0, prefabName.Length - length).TrimEnd(); } internal static bool IsLikelyRuntimeVanillaPrefab(string prefabName) { if (string.IsNullOrWhiteSpace(prefabName)) { return false; } ObjectDB instance = ObjectDB.instance; if ((Object)(object)((instance != null) ? instance.GetItemPrefab(prefabName) : null) != (Object)null) { return true; } ZNetScene instance2 = ZNetScene.instance; if ((Object)(object)((instance2 != null) ? instance2.GetPrefab(prefabName) : null) != (Object)null) { return true; } if ((Object)(object)ZoneSystem.instance == (Object)null) { return false; } foreach (ZoneLocation location in ZoneSystem.instance.m_locations) { if (string.Equals((location?.m_prefabName ?? location?.m_prefab.Name ?? "").Trim(), prefabName, StringComparison.OrdinalIgnoreCase)) { return true; } } return false; } } internal sealed class PluginBootstrapCoordinator { private readonly DropNSpawnPlugin _host; internal PluginBootstrapCoordinator(DropNSpawnPlugin host) { _host = host; } internal void Run() { PluginManifestCoordinator.Initialize(DropNSpawnPlugin.ConfigSync); bool saveOnConfigSet = ((BaseUnityPlugin)_host).Config.SaveOnConfigSet; ((BaseUnityPlugin)_host).Config.SaveOnConfigSet = false; try { BindConfigurationEntries(); InitializeCoordinators(); AttachReloadAndManifestHandlers(); InitializeRuntimeSystems(); ApplyPatchesAndWatchers(); ((BaseUnityPlugin)_host).Config.Save(); } finally { if (saveOnConfigSet) { ((BaseUnityPlugin)_host).Config.SaveOnConfigSet = saveOnConfigSet; } } } private void BindConfigurationEntries() { PluginBoundSettings.ServerConfigLocked = _host.BindConfigEntry("1 - General", "Lock Configuration", DropNSpawnPlugin.Toggle.On, "If on, the configuration is locked and can be changed by server admins only.", synchronizedSetting: true, 800); PluginBoundSettings.DisableGlobalKeySpawnSystemEntriesInLowTierBiomes = _host.BindConfigEntry("1 - General", "Disable global key SpawnSystem entries in low tier biomes", DropNSpawnPlugin.Toggle.Off, "If on, SpawnSystem entries with requiredGlobalKey in Meadows, BlackForest, Swamp, Mountain, or Plains are disabled globally. This prevents global keys from making high tier monsters appear in low tier biomes.", synchronizedSetting: true, 700); SpawnerGlobalConfig.Bind(_host); CharacterDropGlobalConfig.Bind(_host); EventGlobalConfig.Bind(_host); BindDomainConfigurationEntries(); } private void BindDomainConfigurationEntries() { PluginBoundSettings.EnableCharacterOverrides = _host.BindConfigEntry("4 - Domains", "Enable Character Overrides", DropNSpawnPlugin.Toggle.On, "If off, DropNSpawn character YAML files stay on disk but CharacterDrop runtime overrides are not applied and existing character changes are restored to vanilla. Turn this off with Enable Object when using Drop That!. Turn this off when using Spawner Tweaks creature overrides.", synchronizedSetting: true, 700); PluginBoundSettings.EnableObjectOverrides = _host.BindConfigEntry("4 - Domains", "Enable Object Overrides", DropNSpawnPlugin.Toggle.On, "If off, DropNSpawn object YAML files stay on disk but object runtime overrides are not applied and existing object changes are restored to vanilla. Turn this off with Enable Character when using Drop That!. Turn this off when using Spawner Tweaks features for Chests or Pickables.", synchronizedSetting: true, 600); PluginBoundSettings.EnableSpawnerOverrides = _host.BindConfigEntry("4 - Domains", "Enable Spawner Overrides", DropNSpawnPlugin.Toggle.On, "If off, DropNSpawn SpawnArea and CreatureSpawner runtime overrides are not applied and existing spawner changes are restored to vanilla. Turn this off with Enable SpawnSystem when using Spawn That!. Turn this off when using Spawner Tweaks Spawn points or Spawners features.", synchronizedSetting: true, 500); PluginBoundSettings.EnableSpawnSystemOverrides = _host.BindConfigEntry("4 - Domains", "Enable SpawnSystem Overrides", DropNSpawnPlugin.Toggle.On, "If off, DropNSpawn world SpawnSystem runtime overrides and extended global key handling are not applied and existing SpawnSystem changes are restored to vanilla. Turn this off for Expand World Spawns. Turn this off with Enable Spawner when using Spawn That! world spawning.", synchronizedSetting: true, 300); PluginBoundSettings.EnableEventOverrides = _host.BindConfigEntry("4 - Domains", "Enable Event Overrides", DropNSpawnPlugin.Toggle.On, "If off, DropNSpawn event YAML files stay on disk but RandEventSystem event overrides are not applied and existing event changes are restored to vanilla.", synchronizedSetting: true, 200); } private void InitializeCoordinators() { _host.RuntimeWorkCoordinator = new PluginRuntimeWorkCoordinator(_host); _host.ReloadCoordinator = new PluginReloadCoordinator(_host, PluginBoundSettings.EnableObjectOverrides, PluginBoundSettings.EnableCharacterOverrides, PluginBoundSettings.EnableSpawnerOverrides, PluginBoundSettings.EnableSpawnSystemOverrides, PluginBoundSettings.EnableEventOverrides); } private void AttachReloadAndManifestHandlers() { DropNSpawnPlugin.ConfigSync.AddLockingConfigEntry(PluginBoundSettings.ServerConfigLocked); DropNSpawnPlugin.ConfigSync.SourceOfTruthChanged += _host.ReloadCoordinator.HandleSourceOfTruthChanged; PluginBoundSettings.EnableObjectOverrides.SettingChanged += _host.ReloadCoordinator.HandleDomainToggleSettingChanged; PluginBoundSettings.EnableCharacterOverrides.SettingChanged += _host.ReloadCoordinator.HandleDomainToggleSettingChanged; PluginBoundSettings.EnableSpawnerOverrides.SettingChanged += _host.ReloadCoordinator.HandleDomainToggleSettingChanged; PluginBoundSettings.EnableSpawnSystemOverrides.SettingChanged += _host.ReloadCoordinator.HandleDomainToggleSettingChanged; PluginBoundSettings.EnableEventOverrides.SettingChanged += _host.ReloadCoordinator.HandleDomainToggleSettingChanged; PluginBoundSettings.DisableGlobalKeySpawnSystemEntriesInLowTierBiomes.SettingChanged += HandleSpawnSystemGlobalFilterSettingChanged; PluginManifestCoordinator.AttachRuntimeDomainHandlers(); } internal static void HandleSpawnSystemGlobalFilterSettingChanged(object? sender, EventArgs e) { SpawnSystemManager.ReloadConfiguration(); } private void InitializeRuntimeSystems() { Directory.CreateDirectory(DropNSpawnPlugin.YamlConfigDirectoryPath); NetworkPayloadSyncSupport.Initialize((MonoBehaviour)(object)_host); ExampleContentWriter.EnsureDefaultExampleFiles(); DomainRegistry.InitializeRuntimeDomains(); DropNSpawnConsoleCommands.Register(); } private void ApplyPatchesAndWatchers() { Assembly executingAssembly = Assembly.GetExecutingAssembly(); _host.HarmonyInstance.PatchAll(executingAssembly); EspSpawnSystemCompatibility.Initialize(_host.HarmonyInstance); VneiCompatibility.Initialize(_host.HarmonyInstance); _host.ReloadCoordinator.InitializeWatchers(); } } [BepInPlugin("sighsorry.DropNSpawn", "DropNSpawn", "1.3.6")] [BepInDependency("expand_world_data", "1.66")] [BepInDependency(/*Could not decode attribute arguments.*/)] public class DropNSpawnPlugin : BaseUnityPlugin { [Flags] internal enum ReloadDomain { None = 0, Object = 1, Character = 2, Spawner = 4, SpawnSystem = 8, Event = 0x10, All = 0x1F } internal readonly struct DomainToggleState { internal Toggle Object { get; } internal Toggle Character { get; } internal Toggle Spawner { get; } internal Toggle SpawnSystem { get; } internal Toggle Event { get; } internal DomainToggleState(Toggle @object, Toggle character, Toggle spawner, Toggle spawnSystem, Toggle @event) { Object = @object; Character = character; Spawner = spawner; SpawnSystem = spawnSystem; Event = @event; } } public enum Toggle { On = 1, Off = 0 } private class ConfigurationManagerAttributes { [UsedImplicitly] public int? Order; [UsedImplicitly] public bool? Browsable; [UsedImplicitly] public string? Category; [UsedImplicitly] public Action? CustomDrawer; } internal const string ModName = "DropNSpawn"; internal const string YamlFilePrefix = "DNS"; internal const string ModVersion = "1.3.6"; internal const string Author = "sighsorry"; private const string ModGUID = "sighsorry.DropNSpawn"; private static string ConfigFileName = "sighsorry.DropNSpawn.cfg"; private static string ConfigFileFullPath = Paths.ConfigPath + Path.DirectorySeparatorChar + ConfigFileName; private readonly Harmony _harmony = new Harmony("sighsorry.DropNSpawn"); public static readonly ManualLogSource DropNSpawnLogger = Logger.CreateLogSource("DropNSpawn"); private static ConfigSync? _configSync; private PluginReloadCoordinator? _reloadCoordinator; private PluginRuntimeWorkCoordinator? _runtimeWorkCoordinator; internal static string YamlConfigDirectoryPath => Path.Combine(Paths.ConfigPath, "DropNSpawn"); internal static string YamlRulesWatcherPattern => "DNS_*.*"; internal static string CurrentConfigFileName => ConfigFileName; internal static string CurrentConfigFileFullPath => ConfigFileFullPath; internal static DropNSpawnPlugin? Instance { get; private set; } internal static ConfigSync ConfigSync => _configSync ?? throw new InvalidOperationException("ServerSync has not been initialized yet."); internal static bool IsSourceOfTruth => ConfigSync.IsSourceOfTruth; internal Harmony HarmonyInstance => _harmony; internal PluginReloadCoordinator? ReloadCoordinator { get { return _reloadCoordinator; } set { _reloadCoordinator = value; } } internal PluginRuntimeWorkCoordinator? RuntimeWorkCoordinator { get { return _runtimeWorkCoordinator; } set { _runtimeWorkCoordinator = value; } } public void Awake() { EnsureServerSyncInitialized(); Instance = this; new PluginBootstrapCoordinator(this).Run(); } private void Update() { _runtimeWorkCoordinator?.ProcessUpdateFrame(); } private void OnDestroy() { if ((Object)(object)Instance == (Object)(object)this) { Instance = null; } SaveWithRespectToConfigSet(); if (_configSync != null && _reloadCoordinator != null) { _configSync.SourceOfTruthChanged -= _reloadCoordinator.HandleSourceOfTruthChanged; } if (_reloadCoordinator != null) { ConfigEntry? enableObjectOverrides = PluginBoundSettings.EnableObjectOverrides; if (enableObjectOverrides != null) { enableObjectOverrides.SettingChanged -= _reloadCoordinator.HandleDomainToggleSettingChanged; } ConfigEntry? enableCharacterOverrides = PluginBoundSettings.EnableCharacterOverrides; if (enableCharacterOverrides != null) { enableCharacterOverrides.SettingChanged -= _reloadCoordinator.HandleDomainToggleSettingChanged; } ConfigEntry? enableSpawnerOverrides = PluginBoundSettings.EnableSpawnerOverrides; if (enableSpawnerOverrides != null) { enableSpawnerOverrides.SettingChanged -= _reloadCoordinator.HandleDomainToggleSettingChanged; } ConfigEntry? enableSpawnSystemOverrides = PluginBoundSettings.EnableSpawnSystemOverrides; if (enableSpawnSystemOverrides != null) { enableSpawnSystemOverrides.SettingChanged -= _reloadCoordinator.HandleDomainToggleSettingChanged; } ConfigEntry? enableEventOverrides = PluginBoundSettings.EnableEventOverrides; if (enableEventOverrides != null) { enableEventOverrides.SettingChanged -= _reloadCoordinator.HandleDomainToggleSettingChanged; } ConfigEntry? disableGlobalKeySpawnSystemEntriesInLowTierBiomes = PluginBoundSettings.DisableGlobalKeySpawnSystemEntriesInLowTierBiomes; if (disableGlobalKeySpawnSystemEntriesInLowTierBiomes != null) { disableGlobalKeySpawnSystemEntriesInLowTierBiomes.SettingChanged -= PluginBootstrapCoordinator.HandleSpawnSystemGlobalFilterSettingChanged; } } PluginManifestCoordinator.DetachRuntimeDomainHandlers(); _runtimeWorkCoordinator?.Dispose(); _runtimeWorkCoordinator = null; _reloadCoordinator?.Dispose(); _reloadCoordinator = null; CharacterDropManager.ResetWorldRuntimeState(); SpawnerGlobalConfig.Dispose(); PluginBoundSettings.Clear(); EventManager.Dispose(); NetworkPayloadSyncSupport.Shutdown(); } private static void EnsureServerSyncInitialized() { if (_configSync == null) { _configSync = new ConfigSync("sighsorry.DropNSpawn") { DisplayName = "DropNSpawn", CurrentVersion = "1.3.6", MinimumRequiredVersion = "1.3.6" }; } } internal void SaveWithRespectToConfigSet(bool reload = false, bool save = true) { bool saveOnConfigSet = ((BaseUnityPlugin)this).Config.SaveOnConfigSet; ((BaseUnityPlugin)this).Config.SaveOnConfigSet = false; try { if (reload) { ((BaseUnityPlugin)this).Config.Reload(); } if (save) { ((BaseUnityPlugin)this).Config.Save(); } } finally { ((BaseUnityPlugin)this).Config.SaveOnConfigSet = saveOnConfigSet; } } internal static bool IsRuntimeServer() { if ((Object)(object)ZNet.instance != (Object)null) { return ZNet.instance.IsServer(); } return false; } internal static void QueueGameDataRefresh(ReloadDomain domains, string source) { Instance?._runtimeWorkCoordinator?.QueueGameDataRefresh(domains, source); } internal static bool IsGameDataRefreshDeferred(ReloadDomain domain) { DropNSpawnPlugin? instance = Instance; if (instance == null) { return false; } return instance._runtimeWorkCoordinator?.IsGameDataRefreshDeferred(domain) == true; } internal ConfigEntry BindConfigEntry(string group, string name, T value, ConfigDescription description, bool synchronizedSetting = true, int? configManagerOrder = null) { //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Expected O, but got Unknown ConfigDescription val = new ConfigDescription(description.Description + (synchronizedSetting ? " [Synced with Server]" : " [Not Synced with Server]"), description.AcceptableValues, BuildConfigDescriptionTags(description.Tags, configManagerOrder)); ConfigEntry val2 = ((BaseUnityPlugin)this).Config.Bind(group, name, value, val); ConfigSync.AddConfigEntry(val2).SynchronizedConfig = synchronizedSetting; return val2; } internal ConfigEntry BindConfigEntry(string group, string name, T value, string description, bool synchronizedSetting = true, int? configManagerOrder = null) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Expected O, but got Unknown return BindConfigEntry(group, name, value, new ConfigDescription(description, (AcceptableValueBase)null, Array.Empty()), synchronizedSetting, configManagerOrder); } private static object[] BuildConfigDescriptionTags(object[]? existingTags, int? configManagerOrder) { if (!configManagerOrder.HasValue) { return existingTags ?? Array.Empty(); } return (existingTags ?? Array.Empty()).Concat(new object[1] { new ConfigurationManagerAttributes { Order = configManagerOrder.Value } }).ToArray(); } } public static class ToggleExtentions { [SpecialName] public sealed class $A5DFA62E049B860EDABA9962FA410667 { [SpecialName] public static class $B6EBA18AB32C5C8A4BB623283A60F3A3 { } [ExtensionMarker("$B6EBA18AB32C5C8A4BB623283A60F3A3")] public bool IsOn() { throw new NotSupportedException(); } [ExtensionMarker("$B6EBA18AB32C5C8A4BB623283A60F3A3")] public bool IsOff() { throw new NotSupportedException(); } } public static bool IsOn(this DropNSpawnPlugin.Toggle value) { return value == DropNSpawnPlugin.Toggle.On; } public static bool IsOff(this DropNSpawnPlugin.Toggle value) { return value == DropNSpawnPlugin.Toggle.Off; } } internal static class PluginManifestCoordinator { private static readonly Dictionary> SyncedManifests = new Dictionary>(); private static readonly Dictionary SyncedManifestChangedHandlers = new Dictionary(); internal static void Initialize(ConfigSync configSync) { SyncedManifests.Clear(); SyncedManifestChangedHandlers.Clear(); DomainDescriptor[] runtimeDomains = DomainRegistry.RuntimeDomains; foreach (DomainDescriptor domainDescriptor in runtimeDomains) { SyncedManifests[domainDescriptor.ReloadDomain] = new CustomSyncedValue(configSync, domainDescriptor.ManifestSettingKey, "", domainDescriptor.ManifestPriority); DomainDescriptor capturedDomain = domainDescriptor; SyncedManifestChangedHandlers[domainDescriptor.ReloadDomain] = delegate { HandleSyncedManifestChanged(capturedDomain); }; } } internal static void AttachRuntimeDomainHandlers() { DomainDescriptor[] runtimeDomains = DomainRegistry.RuntimeDomains; foreach (DomainDescriptor domain in runtimeDomains) { GetSyncedManifestEntry(domain).ValueChanged += GetSyncedManifestChangedHandler(domain); } } internal static void DetachRuntimeDomainHandlers() { DomainDescriptor[] runtimeDomains = DomainRegistry.RuntimeDomains; foreach (DomainDescriptor domainDescriptor in runtimeDomains) { if (SyncedManifests.TryGetValue(domainDescriptor.ReloadDomain, out CustomSyncedValue value) && SyncedManifestChangedHandlers.TryGetValue(domainDescriptor.ReloadDomain, out Action value2)) { value.ValueChanged -= value2; } } } private static string GetSyncedManifestValue(DomainDescriptor domain) { return GetSyncedManifestEntry(domain).Value ?? ""; } internal static bool TryGetSyncedEntries(DomainDescriptor domain, out List entries, out string payloadToken) { return NetworkPayloadSyncSupport.TryGetEntries(domain, GetSyncedManifestValue(domain), out entries, out payloadToken); } internal static void PublishSyncedPayload(DomainDescriptor domain, List entries, string? knownSignature) { NetworkPayloadSyncSupport.PublishPayloadAsync(domain, entries, knownSignature, delegate(string manifest) { AssignServerManifestValue(GetSyncedManifestEntry(domain), manifest); }); } internal static void EnterClientAuthorityCutover() { DomainDescriptor[] runtimeDomains = DomainRegistry.RuntimeDomains; for (int i = 0; i < runtimeDomains.Length; i++) { runtimeDomains[i].OnClientAuthorityCutover?.Invoke(); } } internal static void ReplayCurrentSyncedManifestStates() { if (!DropNSpawnPlugin.IsSourceOfTruth) { DomainDescriptor[] runtimeDomains = DomainRegistry.RuntimeDomains; for (int i = 0; i < runtimeDomains.Length; i++) { HandleSyncedManifestChanged(runtimeDomains[i]); } } } private static CustomSyncedValue GetSyncedManifestEntry(DomainDescriptor domain) { if (SyncedManifests.TryGetValue(domain.ReloadDomain, out CustomSyncedValue value)) { return value; } throw new InvalidOperationException("ServerSync " + domain.DomainKey + " payload has not been initialized yet."); } private static Action GetSyncedManifestChangedHandler(DomainDescriptor domain) { if (SyncedManifestChangedHandlers.TryGetValue(domain.ReloadDomain, out Action value)) { return value; } throw new InvalidOperationException("ServerSync " + domain.DomainKey + " manifest handler has not been initialized yet."); } private static void HandleSyncedManifestChanged(DomainDescriptor domain) { if (!DropNSpawnPlugin.IsSourceOfTruth) { domain.BeforeClientManifestChanged?.Invoke(); domain.HandleManifestChanged(GetSyncedManifestValue(domain)); } } private static void AssignServerManifestValue(CustomSyncedValue syncedValue, string manifest) { if (manifest == null) { manifest = ""; } if (!string.Equals(syncedValue.Value ?? "", manifest, StringComparison.Ordinal)) { syncedValue.AssignLocalValue(manifest); } } } internal sealed class PluginReloadCoordinator { private const float FileReloadDebounceSeconds = 0.5f; private readonly DropNSpawnPlugin _host; private readonly (ConfigEntry Entry, DropNSpawnPlugin.ReloadDomain Domain)[] _domainToggles; private readonly object _reloadLock = new object(); private readonly object _reloadDebounceLock = new object(); private FileSystemWatcher? _configWatcher; private FileSystemWatcher? _rulesWatcher; private Coroutine? _configReloadCoroutine; private Coroutine? _rulesReloadCoroutine; private DropNSpawnPlugin.ReloadDomain _pendingRuleReloadDomains; private bool _configReloadPending; private float _lastQueuedConfigReloadTime; private float _lastQueuedRuleReloadTime; internal bool IsConfigEntryReloadSuppressed { get; private set; } internal PluginReloadCoordinator(DropNSpawnPlugin host, ConfigEntry enableObjectOverrides, ConfigEntry enableCharacterOverrides, ConfigEntry enableSpawnerOverrides, ConfigEntry enableSpawnSystemOverrides, ConfigEntry enableEventOverrides) { _host = host; _domainToggles = new(ConfigEntry, DropNSpawnPlugin.ReloadDomain)[5] { (enableObjectOverrides, DropNSpawnPlugin.ReloadDomain.Object), (enableCharacterOverrides, DropNSpawnPlugin.ReloadDomain.Character), (enableSpawnerOverrides, DropNSpawnPlugin.ReloadDomain.Spawner), (enableSpawnSystemOverrides, DropNSpawnPlugin.ReloadDomain.SpawnSystem), (enableEventOverrides, DropNSpawnPlugin.ReloadDomain.Event) }; } internal void HandleSourceOfTruthChanged(bool isSourceOfTruth) { NetworkPayloadSyncSupport.HandleSourceOfTruthChanged(isSourceOfTruth); if (!isSourceOfTruth) { PluginManifestCoordinator.EnterClientAuthorityCutover(); PluginManifestCoordinator.ReplayCurrentSyncedManifestStates(); } UpdateRulesWatcherState(DropNSpawnPlugin.IsSourceOfTruth); ReloadDomains(DropNSpawnPlugin.ReloadDomain.All); } internal void HandleDomainToggleSettingChanged(object? sender, EventArgs e) { if (!IsConfigEntryReloadSuppressed) { ReloadDomains(GetReloadDomainForToggleSetting(sender)); } } internal void InitializeWatchers() { SetupConfigWatcher(); SetupRulesWatcher(); } internal void Dispose() { _configWatcher?.Dispose(); _rulesWatcher?.Dispose(); if (_configReloadCoroutine != null) { ((MonoBehaviour)_host).StopCoroutine(_configReloadCoroutine); _configReloadCoroutine = null; } if (_rulesReloadCoroutine != null) { ((MonoBehaviour)_host).StopCoroutine(_rulesReloadCoroutine); _rulesReloadCoroutine = null; } } internal void UpdateRulesWatcherState(bool isSourceOfTruth) { if (_rulesWatcher != null) { _rulesWatcher.EnableRaisingEvents = isSourceOfTruth; } } private void SetupConfigWatcher() { _configWatcher = new FileSystemWatcher(Paths.ConfigPath, DropNSpawnPlugin.CurrentConfigFileName); _configWatcher.Changed += OnConfigFileChanged; _configWatcher.Created += OnConfigFileChanged; _configWatcher.Renamed += OnConfigFileChanged; _configWatcher.IncludeSubdirectories = true; _configWatcher.SynchronizingObject = ThreadingHelper.SynchronizingObject; _configWatcher.EnableRaisingEvents = true; } private void SetupRulesWatcher() { Directory.CreateDirectory(DropNSpawnPlugin.YamlConfigDirectoryPath); _rulesWatcher = new FileSystemWatcher(DropNSpawnPlugin.YamlConfigDirectoryPath, ObjectDropManager.RulesWatcherPattern); _rulesWatcher.Changed += OnRuleFileChanged; _rulesWatcher.Created += OnRuleFileChanged; _rulesWatcher.Deleted += OnRuleFileChanged; _rulesWatcher.Renamed += OnRuleFileChanged; _rulesWatcher.IncludeSubdirectories = true; _rulesWatcher.SynchronizingObject = ThreadingHelper.SynchronizingObject; UpdateRulesWatcherState(DropNSpawnPlugin.IsSourceOfTruth); } private void OnConfigFileChanged(object sender, FileSystemEventArgs e) { QueueConfigReload(); } private void OnRuleFileChanged(object sender, FileSystemEventArgs e) { if (!DropNSpawnPlugin.IsSourceOfTruth) { return; } DropNSpawnPlugin.ReloadDomain reloadDomain = DropNSpawnPlugin.ReloadDomain.None; DomainDescriptor[] runtimeDomains = DomainRegistry.RuntimeDomains; foreach (DomainDescriptor domainDescriptor in runtimeDomains) { bool flag = domainDescriptor.ShouldReloadForPath(e.FullPath); if (!flag && e is RenamedEventArgs e2) { flag = domainDescriptor.ShouldReloadForPath(e2.OldFullPath); } if (flag) { reloadDomain |= domainDescriptor.ReloadDomain; } } if (reloadDomain != DropNSpawnPlugin.ReloadDomain.None) { QueueRuleReload(reloadDomain); } } private void QueueConfigReload() { lock (_reloadDebounceLock) { _configReloadPending = true; _lastQueuedConfigReloadTime = Time.realtimeSinceStartup; if (_configReloadCoroutine == null) { _configReloadCoroutine = ((MonoBehaviour)_host).StartCoroutine(ProcessQueuedConfigReload()); } } } private void QueueRuleReload(DropNSpawnPlugin.ReloadDomain domains) { if (domains == DropNSpawnPlugin.ReloadDomain.None) { return; } lock (_reloadDebounceLock) { _pendingRuleReloadDomains |= domains; _lastQueuedRuleReloadTime = Time.realtimeSinceStartup; if (_rulesReloadCoroutine == null) { _rulesReloadCoroutine = ((MonoBehaviour)_host).StartCoroutine(ProcessQueuedRuleReload()); } } } private IEnumerator ProcessQueuedConfigReload() { while (true) { float lastQueuedConfigReloadTime; lock (_reloadDebounceLock) { lastQueuedConfigReloadTime = _lastQueuedConfigReloadTime; } while (Time.realtimeSinceStartup - lastQueuedConfigReloadTime < 0.5f) { yield return null; lock (_reloadDebounceLock) { lastQueuedConfigReloadTime = _lastQueuedConfigReloadTime; } } bool configReloadPending; lock (_reloadDebounceLock) { configReloadPending = _configReloadPending; _configReloadPending = false; } if (configReloadPending) { ExecuteQueuedConfigReload(); } lock (_reloadDebounceLock) { if (_configReloadPending) { continue; } _configReloadCoroutine = null; break; } } } private IEnumerator ProcessQueuedRuleReload() { while (true) { float lastQueuedRuleReloadTime; lock (_reloadDebounceLock) { lastQueuedRuleReloadTime = _lastQueuedRuleReloadTime; } while (Time.realtimeSinceStartup - lastQueuedRuleReloadTime < 0.5f) { yield return null; lock (_reloadDebounceLock) { lastQueuedRuleReloadTime = _lastQueuedRuleReloadTime; } } DropNSpawnPlugin.ReloadDomain pendingRuleReloadDomains; lock (_reloadDebounceLock) { pendingRuleReloadDomains = _pendingRuleReloadDomains; _pendingRuleReloadDomains = DropNSpawnPlugin.ReloadDomain.None; } if (pendingRuleReloadDomains != DropNSpawnPlugin.ReloadDomain.None) { ExecuteQueuedRuleReload(pendingRuleReloadDomains); } lock (_reloadDebounceLock) { if (_pendingRuleReloadDomains != DropNSpawnPlugin.ReloadDomain.None) { continue; } _rulesReloadCoroutine = null; break; } } } private void ExecuteQueuedConfigReload() { lock (_reloadLock) { if (!File.Exists(DropNSpawnPlugin.CurrentConfigFileFullPath)) { DropNSpawnPlugin.DropNSpawnLogger.LogWarning((object)"Config file does not exist. Skipping reload."); return; } try { DropNSpawnPlugin.DomainToggleState previous = CaptureDomainToggleState(); IsConfigEntryReloadSuppressed = true; _host.SaveWithRespectToConfigSet(reload: true, save: false); ReloadDomains(GetChangedDomainToggles(previous, CaptureDomainToggleState())); DropNSpawnPlugin.DropNSpawnLogger.LogInfo((object)"Configuration reload complete."); } catch (Exception ex) { DropNSpawnPlugin.DropNSpawnLogger.LogError((object)("Error reloading configuration: " + ex.Message)); } finally { IsConfigEntryReloadSuppressed = false; } } } private void ExecuteQueuedRuleReload(DropNSpawnPlugin.ReloadDomain domains) { if (domains == DropNSpawnPlugin.ReloadDomain.None || !DropNSpawnPlugin.IsSourceOfTruth) { return; } lock (_reloadLock) { try { ReloadDomains(domains); DropNSpawnPlugin.DropNSpawnLogger.LogInfo((object)"Override YAML configuration reload complete."); } catch (Exception ex) { DropNSpawnPlugin.DropNSpawnLogger.LogError((object)("Error reloading override YAML configuration: " + ex.Message)); } } } private DropNSpawnPlugin.DomainToggleState CaptureDomainToggleState() { return new DropNSpawnPlugin.DomainToggleState(GetDomainToggleValue(DropNSpawnPlugin.ReloadDomain.Object), GetDomainToggleValue(DropNSpawnPlugin.ReloadDomain.Character), GetDomainToggleValue(DropNSpawnPlugin.ReloadDomain.Spawner), GetDomainToggleValue(DropNSpawnPlugin.ReloadDomain.SpawnSystem), GetDomainToggleValue(DropNSpawnPlugin.ReloadDomain.Event)); } private DropNSpawnPlugin.ReloadDomain GetReloadDomainForToggleSetting(object? sender) { DropNSpawnPlugin.ReloadDomain reloadDomain = DropNSpawnPlugin.ReloadDomain.None; (ConfigEntry, DropNSpawnPlugin.ReloadDomain)[] domainToggles = _domainToggles; for (int i = 0; i < domainToggles.Length; i++) { var (val, reloadDomain2) = domainToggles[i]; if (sender == val) { reloadDomain |= reloadDomain2; } } return reloadDomain; } private DropNSpawnPlugin.Toggle GetDomainToggleValue(DropNSpawnPlugin.ReloadDomain domain) { (ConfigEntry, DropNSpawnPlugin.ReloadDomain)[] domainToggles = _domainToggles; for (int i = 0; i < domainToggles.Length; i++) { (ConfigEntry, DropNSpawnPlugin.ReloadDomain) tuple = domainToggles[i]; var (val, _) = tuple; if (tuple.Item2 == domain) { return val.Value; } } return DropNSpawnPlugin.Toggle.Off; } private static DropNSpawnPlugin.ReloadDomain GetChangedDomainToggles(DropNSpawnPlugin.DomainToggleState previous, DropNSpawnPlugin.DomainToggleState current) { DropNSpawnPlugin.ReloadDomain reloadDomain = DropNSpawnPlugin.ReloadDomain.None; if (previous.Object != current.Object) { reloadDomain |= DropNSpawnPlugin.ReloadDomain.Object; } if (previous.Character != current.Character) { reloadDomain |= DropNSpawnPlugin.ReloadDomain.Character; } if (previous.Spawner != current.Spawner) { reloadDomain |= DropNSpawnPlugin.ReloadDomain.Spawner; } if (previous.SpawnSystem != current.SpawnSystem) { reloadDomain |= DropNSpawnPlugin.ReloadDomain.SpawnSystem; } if (previous.Event != current.Event) { reloadDomain |= DropNSpawnPlugin.ReloadDomain.Event; } return reloadDomain; } private static void ReloadDomains(DropNSpawnPlugin.ReloadDomain domains) { DomainDescriptor[] runtimeDomains = DomainRegistry.RuntimeDomains; foreach (DomainDescriptor domainDescriptor in runtimeDomains) { if ((domains & domainDescriptor.ReloadDomain) != DropNSpawnPlugin.ReloadDomain.None) { domainDescriptor.Reload(); } } } } internal sealed class PluginRuntimeWorkCoordinator { private const float GameDataRefreshDebounceSeconds = 0.1f; private const double RuntimeWorkFrameBudgetSeconds = 0.001; private const int WorkLaneCount = 3; private readonly DropNSpawnPlugin _host; private readonly object _gameDataRefreshLock = new object(); private Coroutine? _gameDataRefreshCoroutine; private DropNSpawnPlugin.ReloadDomain _deferredGameDataRefreshDomains; private DropNSpawnPlugin.ReloadDomain _pendingGameDataRefreshDomains; private float _lastQueuedGameDataRefreshTime; private int _reconcileRoundRobinCursor; private int _snapshotBuildRoundRobinCursor; private int _workLaneRoundRobinCursor; private bool _hasObservedExpandWorldDataReadyState; private bool _lastObservedExpandWorldDataReadyState = true; internal PluginRuntimeWorkCoordinator(DropNSpawnPlugin host) { _host = host; } internal void Dispose() { if (_gameDataRefreshCoroutine != null) { ((MonoBehaviour)_host).StopCoroutine(_gameDataRefreshCoroutine); _gameDataRefreshCoroutine = null; } } internal void ProcessUpdateFrame() { ObserveExpandWorldDataReadyTransition(); if (NetworkPayloadSyncSupport.HasPendingWork() || HasPendingSnapshotBuildWork() || HasPendingReconcileWork()) { double num = Time.realtimeSinceStartupAsDouble + 0.001; int num2 = 0; while (Time.realtimeSinceStartupAsDouble < num && num2 < 5) { num2 = ((!ProcessNextPendingWorkLane(num)) ? (num2 + 1) : 0); } } } internal void QueueGameDataRefresh(DropNSpawnPlugin.ReloadDomain domains, string source) { if (domains == DropNSpawnPlugin.ReloadDomain.None) { return; } lock (_gameDataRefreshLock) { _pendingGameDataRefreshDomains |= domains; _deferredGameDataRefreshDomains |= domains; _lastQueuedGameDataRefreshTime = Time.realtimeSinceStartup; if (_gameDataRefreshCoroutine == null) { _gameDataRefreshCoroutine = ((MonoBehaviour)_host).StartCoroutine(ProcessQueuedGameDataRefresh(source)); } } } internal bool IsGameDataRefreshDeferred(DropNSpawnPlugin.ReloadDomain domain) { if (domain == DropNSpawnPlugin.ReloadDomain.None) { return false; } lock (_gameDataRefreshLock) { return (_deferredGameDataRefreshDomains & domain) != 0; } } private void ObserveExpandWorldDataReadyTransition() { if (!BiomeResolutionSupport.IsExpandWorldDataPresent()) { return; } bool flag = BiomeResolutionSupport.IsExpandWorldDataReadyOrUnavailable(); bool flag2 = false; if (!_hasObservedExpandWorldDataReadyState) { _hasObservedExpandWorldDataReadyState = true; _lastObservedExpandWorldDataReadyState = flag; flag2 = flag; } else if (flag && !_lastObservedExpandWorldDataReadyState) { _lastObservedExpandWorldDataReadyState = true; flag2 = true; } else { _lastObservedExpandWorldDataReadyState = flag; } if (flag2 && DropNSpawnPlugin.IsSourceOfTruth) { DomainDescriptor[] runtimeDomains = DomainRegistry.RuntimeDomains; for (int i = 0; i < runtimeDomains.Length; i++) { runtimeDomains[i].HandleExpandWorldDataReady(); } } } private bool ProcessNextPendingWorkLane(double deadline) { for (int i = 0; i < 3; i++) { int num = (_workLaneRoundRobinCursor + i) % 3; if (HasPendingWork(num)) { _workLaneRoundRobinCursor = (num + 1) % 3; if (ProcessPendingWork(num, deadline)) { return true; } } } return false; } private bool ProcessNextPendingSnapshotBuildStep(double deadline) { for (int i = 0; i < DomainRegistry.SnapshotBuildDomains.Length; i++) { int num = (_snapshotBuildRoundRobinCursor + i) % DomainRegistry.SnapshotBuildDomains.Length; DomainDescriptor domainDescriptor = DomainRegistry.SnapshotBuildDomains[num]; if (domainDescriptor.HasPendingSnapshotBuildWork != null && domainDescriptor.ProcessPendingSnapshotBuildStep != null && domainDescriptor.HasPendingSnapshotBuildWork()) { _snapshotBuildRoundRobinCursor = (num + 1) % DomainRegistry.SnapshotBuildDomains.Length; return domainDescriptor.ProcessPendingSnapshotBuildStep(deadline); } } return false; } private bool HasPendingWork(int lane) { return lane switch { 0 => NetworkPayloadSyncSupport.HasPendingWork(), 1 => HasPendingSnapshotBuildWork(), _ => HasPendingReconcileWork(), }; } private bool ProcessPendingWork(int lane, double deadline) { return lane switch { 0 => NetworkPayloadSyncSupport.ProcessPendingWork(deadline), 1 => ProcessNextPendingSnapshotBuildStep(deadline), _ => ProcessNextQueuedReconcileStep(deadline), }; } private bool ProcessNextQueuedReconcileStep(double deadline) { for (int i = 0; i < DomainRegistry.ReconcileDomains.Length; i++) { int num = (_reconcileRoundRobinCursor + i) % DomainRegistry.ReconcileDomains.Length; DomainDescriptor domainDescriptor = DomainRegistry.ReconcileDomains[num]; if (domainDescriptor.HasPendingReconcileWork != null && domainDescriptor.ProcessPendingReconcileStep != null && domainDescriptor.HasPendingReconcileWork()) { _reconcileRoundRobinCursor = (num + 1) % DomainRegistry.ReconcileDomains.Length; return domainDescriptor.ProcessPendingReconcileStep(deadline); } } return false; } private bool HasPendingReconcileWork() { DomainDescriptor[] reconcileDomains = DomainRegistry.ReconcileDomains; foreach (DomainDescriptor domainDescriptor in reconcileDomains) { if (domainDescriptor.HasPendingReconcileWork != null && domainDescriptor.ProcessPendingReconcileStep != null && domainDescriptor.HasPendingReconcileWork()) { return true; } } return false; } private bool HasPendingSnapshotBuildWork() { DomainDescriptor[] snapshotBuildDomains = DomainRegistry.SnapshotBuildDomains; foreach (DomainDescriptor domainDescriptor in snapshotBuildDomains) { if (domainDescriptor.HasPendingSnapshotBuildWork != null && domainDescriptor.ProcessPendingSnapshotBuildStep != null && domainDescriptor.HasPendingSnapshotBuildWork()) { return true; } } return false; } private IEnumerator ProcessQueuedGameDataRefresh(string initialSource) { string source = initialSource; while (true) { float lastQueuedGameDataRefreshTime; lock (_gameDataRefreshLock) { lastQueuedGameDataRefreshTime = _lastQueuedGameDataRefreshTime; } while (Time.realtimeSinceStartup - lastQueuedGameDataRefreshTime < 0.1f) { yield return null; lock (_gameDataRefreshLock) { lastQueuedGameDataRefreshTime = _lastQueuedGameDataRefreshTime; } } DropNSpawnPlugin.ReloadDomain domains; lock (_gameDataRefreshLock) { domains = _pendingGameDataRefreshDomains; _pendingGameDataRefreshDomains = DropNSpawnPlugin.ReloadDomain.None; } if (domains == DropNSpawnPlugin.ReloadDomain.None) { break; } PrefabOwnerResolver.Invalidate(); DomainDescriptor[] runtimeDomains = DomainRegistry.RuntimeDomains; foreach (DomainDescriptor domainDescriptor in runtimeDomains) { if ((domains & domainDescriptor.ReloadDomain) != DropNSpawnPlugin.ReloadDomain.None) { domainDescriptor.OnGameDataReady(source); MarkGameDataRefreshDomainProcessed(domainDescriptor.ReloadDomain); if (ShouldYieldBetweenQueuedGameDataRefreshDomains(domains, domainDescriptor.ReloadDomain)) { yield return null; } } } source = "queued game-data refresh"; } lock (_gameDataRefreshLock) { _gameDataRefreshCoroutine = null; } } private bool ShouldYieldBetweenQueuedGameDataRefreshDomains(DropNSpawnPlugin.ReloadDomain domains, DropNSpawnPlugin.ReloadDomain processedDomain) { if ((domains & ~processedDomain) != DropNSpawnPlugin.ReloadDomain.None) { return true; } lock (_gameDataRefreshLock) { return _pendingGameDataRefreshDomains != DropNSpawnPlugin.ReloadDomain.None; } } private void MarkGameDataRefreshDomainProcessed(DropNSpawnPlugin.ReloadDomain domain) { lock (_gameDataRefreshLock) { if ((_pendingGameDataRefreshDomains & domain) == 0) { _deferredGameDataRefreshDomains &= ~domain; } } } } internal static class PluginBoundSettings { internal static ConfigEntry? ServerConfigLocked { get; set; } internal static ConfigEntry? EnableObjectOverrides { get; set; } internal static ConfigEntry? EnableCharacterOverrides { get; set; } internal static ConfigEntry? EnableSpawnerOverrides { get; set; } internal static ConfigEntry? EnableSpawnSystemOverrides { get; set; } internal static ConfigEntry? EnableEventOverrides { get; set; } internal static ConfigEntry? DisableGlobalKeySpawnSystemEntriesInLowTierBiomes { get; set; } internal static void Clear() { ServerConfigLocked = null; EnableObjectOverrides = null; EnableCharacterOverrides = null; EnableSpawnerOverrides = null; EnableSpawnSystemOverrides = null; EnableEventOverrides = null; DisableGlobalKeySpawnSystemEntriesInLowTierBiomes = null; } } internal static class PluginSettingsFacade { internal static bool IsObjectDomainEnabled() { ConfigEntry? enableObjectOverrides = PluginBoundSettings.EnableObjectOverrides; if (enableObjectOverrides == null) { return true; } return enableObjectOverrides.Value != DropNSpawnPlugin.Toggle.Off; } internal static bool IsCharacterDomainEnabled() { ConfigEntry? enableCharacterOverrides = PluginBoundSettings.EnableCharacterOverrides; if (enableCharacterOverrides == null) { return true; } return enableCharacterOverrides.Value != DropNSpawnPlugin.Toggle.Off; } internal static bool IsSpawnerDomainEnabled() { ConfigEntry? enableSpawnerOverrides = PluginBoundSettings.EnableSpawnerOverrides; if (enableSpawnerOverrides == null) { return true; } return enableSpawnerOverrides.Value != DropNSpawnPlugin.Toggle.Off; } internal static bool IsSpawnSystemDomainEnabled() { ConfigEntry? enableSpawnSystemOverrides = PluginBoundSettings.EnableSpawnSystemOverrides; if (enableSpawnSystemOverrides == null) { return true; } return enableSpawnSystemOverrides.Value != DropNSpawnPlugin.Toggle.Off; } internal static bool IsEventDomainEnabled() { ConfigEntry? enableEventOverrides = PluginBoundSettings.EnableEventOverrides; if (enableEventOverrides == null) { return true; } return enableEventOverrides.Value != DropNSpawnPlugin.Toggle.Off; } internal static bool ShouldDisableLowTierBiomeGlobalKeySpawnSystemEntries() { ConfigEntry? disableGlobalKeySpawnSystemEntriesInLowTierBiomes = PluginBoundSettings.DisableGlobalKeySpawnSystemEntriesInLowTierBiomes; if (disableGlobalKeySpawnSystemEntriesInLowTierBiomes == null) { return false; } return disableGlobalKeySpawnSystemEntriesInLowTierBiomes.Value == DropNSpawnPlugin.Toggle.On; } internal static bool IsMultipleEventsEnabled() { return EventGlobalConfig.IsMultipleEventsEnabled(); } internal static bool IsEventCheckPerPlayerEnabled() { return EventGlobalConfig.IsCheckPerPlayerEnabled(); } internal static EventGlobalConfig.EventPlayerBaseDefault GetDefaultEventPlayerBase() { return EventGlobalConfig.GetDefaultPlayerBase(); } internal static float GetMinimumDistanceBetweenEvents() { return EventGlobalConfig.GetMinimumDistanceBetweenEvents(); } internal static float GetEventDurationMultiplier() { return EventGlobalConfig.GetEventDurationMultiplier(); } internal static float GetRandomEventChance() { return EventGlobalConfig.GetRandomEventChance(); } internal static float GetRandomEventIntervalMinutes() { return EventGlobalConfig.GetRandomEventIntervalMinutes(); } internal static bool IsGlobalCharacterDropInStackEnabled() { return CharacterDropGlobalConfig.IsGlobalDropInStackEnabled(); } internal static bool IsMonsterInstantLootDropEnabled() { return CharacterDropGlobalConfig.IsMonsterInstantLootDropEnabled(); } internal static bool IsCharacterDropCalculateChanceLootSystemEnabled() { return CharacterDropGlobalConfig.IsCalculateChanceLootSystemEnabled(); } internal static bool ShouldDisableCharacterLootScalingForCreatureLevelControl() { return CharacterDropGlobalConfig.ShouldDisableLootScalingForCreatureLevelControl(); } internal static int GetCharacterDropAdditionalLootChancePerStar(bool boss) { return CharacterDropGlobalConfig.GetAdditionalLootChancePerStar(boss); } internal static bool IsGlobalCharacterDropTrophyLevelMultiplierEnabled() { return CharacterDropGlobalConfig.IsGlobalTrophyLevelMultiplierEnabled(); } internal static bool IsCharacterDropTrophyLevelMultiplierBlacklisted(string? prefabName) { return CharacterDropGlobalConfig.IsTrophyLevelMultiplierBlacklisted(prefabName); } internal static int GetDefaultSpawnAreaMaxTotalSpawns() { return SpawnerGlobalConfig.GetDefaultSpawnAreaMaxTotalSpawns(); } internal static int GetDefaultZeroCreatureSpawnerRespawnTimeMinutes() { return SpawnerGlobalConfig.GetDefaultZeroCreatureSpawnerRespawnTimeMinutes(); } internal static float GetCharacterDropOnePerPlayerNearbyRange() { return CharacterDropGlobalConfig.GetOnePerPlayerNearbyRange(); } internal static bool IsCharacterDropInStackBlacklisted(string? prefabName) { return CharacterDropGlobalConfig.IsDropInStackBlacklisted(prefabName); } internal static bool IsEligibleOverrideConfigurationPath(string? path) { if (string.IsNullOrWhiteSpace(path)) { return false; } string fullPath = Path.GetFullPath(path); string value = EnsureTrailingSeparator(Path.GetFullPath(DropNSpawnPlugin.YamlConfigDirectoryPath)); return fullPath.StartsWith(value, StringComparison.OrdinalIgnoreCase); } internal static string GetYamlDomainFilePrefix(string domain) { return "DNS_" + domain; } internal static string GetYamlDomainSupplementalPrefix(string domain) { return GetYamlDomainFilePrefix(domain) + "_"; } internal static IEnumerable EnumerateSupplementalOverrideConfigurationPaths(string searchPattern, Func isOverrideFileName) { if (!Directory.Exists(DropNSpawnPlugin.YamlConfigDirectoryPath)) { yield break; } IEnumerable enumerable = (from path in Directory.EnumerateFiles(DropNSpawnPlugin.YamlConfigDirectoryPath, searchPattern, SearchOption.AllDirectories) where IsEligibleOverrideConfigurationPath(path) && isOverrideFileName(Path.GetFileName(path)) select path).OrderBy((string path) => path, StringComparer.OrdinalIgnoreCase); foreach (string item in enumerable) { yield return item; } } private static string EnsureTrailingSeparator(string path) { if (path.EndsWith(Path.DirectorySeparatorChar) || path.EndsWith(Path.AltDirectorySeparatorChar)) { return path; } return path + Path.DirectorySeparatorChar; } } internal static class CharacterDropGlobalConfig { internal enum CharacterLootSystem { Vanilla, CalculateChance } private const string CreatureLevelControlPluginGuid = "org.bepinex.plugins.creaturelevelcontrol"; private const float DefaultOnePerPlayerNearbyRange = 32f; private static readonly object DropInStackBlacklistLock = new object(); private static readonly object TrophyLevelMultiplierBlacklistLock = new object(); private static string _dropInStackBlacklistRaw = ""; private static string _trophyLevelMultiplierBlacklistRaw = ""; private static HashSet _dropInStackBlacklist = new HashSet(StringComparer.OrdinalIgnoreCase); private static HashSet _trophyLevelMultiplierBlacklist = new HashSet(StringComparer.OrdinalIgnoreCase); private static bool? _isCreatureLevelControlLoaded; private static ConfigEntry _monsterInstantLootDrop = null; private static ConfigEntry _characterLootSystem = null; private static ConfigEntry _disableCharacterLootScalingWhenCreatureLevelControlLoaded = null; private static ConfigEntry _additionalLootChancePerStarCreature = null; private static ConfigEntry _additionalLootChancePerStarBoss = null; private static ConfigEntry _globalDropInStack = null; private static ConfigEntry _dropInStackBlacklistEntry = null; private static ConfigEntry _globalTrophyLevelMultiplier = null; private static ConfigEntry _trophyLevelMultiplierBlacklistEntry = null; private static ConfigEntry _onePerPlayerNearbyRange = null; internal static void Bind(DropNSpawnPlugin plugin) { //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Expected O, but got Unknown //IL_00d4: Unknown result type (might be due to invalid IL or missing references) //IL_00e9: Expected O, but got Unknown //IL_010d: Unknown result type (might be due to invalid IL or missing references) //IL_0122: Expected O, but got Unknown _onePerPlayerNearbyRange = plugin.BindConfigEntry("2 - Character", "OnePerPlayer drop check range", 32f, new ConfigDescription("If 0, disables the nearby-player override and uses vanilla server-wide online player count for character-drop onePerPlayer. If greater than 0, counts only living players within this many horizontal XZ meters of the dropping character.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 100f), Array.Empty()), synchronizedSetting: true, 500); _monsterInstantLootDrop = plugin.BindConfigEntry("2 - Character", "monster instant loot drop", DropNSpawnPlugin.Toggle.Off, "If on, monster ragdoll loot saved from CharacterDrop is spawned immediately while the ragdoll remains for its vanilla lifetime. The saved ragdoll loot list is consumed so vanilla ragdoll cleanup does not drop the same items again.", synchronizedSetting: true, 450); _characterLootSystem = plugin.BindConfigEntry("2 - Character", "character loot system", CharacterLootSystem.CalculateChance, "Vanilla leaves character-drop level scaling to the game and YAML levelMultiplier values. CalculateChance replaces vanilla exponential level scaling with configured per-star amount scaling only for non-trophy item drops whose levelMultiplier is true. The global trophy level multiplier setting separately controls trophies and can override their levelMultiplier value.", synchronizedSetting: true, 440); _disableCharacterLootScalingWhenCreatureLevelControlLoaded = plugin.BindConfigEntry("2 - Character", "disable DNS character loot scaling when CLLC is loaded", DropNSpawnPlugin.Toggle.On, "If on and Creature Level & Loot Control is loaded, DropNSpawn does not rewrite this config file but treats character loot system, both per-star loot chance options, and global trophy level multiplier as inactive at runtime. CLLC can then own loot quantities while Character YAML overrides, drop-in-stack, instant loot, and OnePerPlayer range still work. Turn this off only when CLLC loot is Vanilla and DropNSpawn should own loot scaling.", synchronizedSetting: true, 435); _additionalLootChancePerStarCreature = plugin.BindConfigEntry("2 - Character", "chance for additional loot per star for creatures", 50, new ConfigDescription("Percent of an additional successful item drop per creature star when character loot system is CalculateChance. Also controls trophy scaling for non-boss creatures when global trophy level multiplier is on.", (AcceptableValueBase)(object)new AcceptableValueRange(0, 100), Array.Empty()), synchronizedSetting: true, 430); _additionalLootChancePerStarBoss = plugin.BindConfigEntry("2 - Character", "chance for additional loot per star for bosses", 50, new ConfigDescription("Percent of an additional successful item drop per boss star when character loot system is CalculateChance. Also controls trophy scaling for bosses when global trophy level multiplier is on.", (AcceptableValueBase)(object)new AcceptableValueRange(0, 100), Array.Empty()), synchronizedSetting: true, 420); _globalDropInStack = plugin.BindConfigEntry("2 - Character", "global drop in stack", DropNSpawnPlugin.Toggle.Off, "If on, all character loot drops in stacks whenever possible, including vanilla drops that are not overridden in YAML. Items listed in global drop in stack blacklist always stay as separate drops. Non-stackable items and single-quantity drops are unchanged. Turning this off only disables the global default; per-entry YAML dropInStack still works unless the item is blacklisted.", synchronizedSetting: true, 400); _dropInStackBlacklistEntry = plugin.BindConfigEntry("2 - Character", "global drop in stack blacklist", "", "Comma, semicolon, or newline separated item prefab names that should never use character loot drop-in-stack. Applies to both vanilla character drops and YAML-driven character drops when they pass through CharacterDrop. This blacklist has higher priority than the global default and higher priority than per-entry YAML dropInStack. Example: Coins,TrophyDeer", synchronizedSetting: true, 300); _globalTrophyLevelMultiplier = plugin.BindConfigEntry("2 - Character", "global trophy level multiplier", DropNSpawnPlugin.Toggle.Off, "If on, successful character trophy drops keep their original drop chance and scale their final amount by the configured creature or boss additional-loot chance per star. Eligible trophies are kept out of vanilla levelMultiplier amount scaling to avoid very large 2^(level-1) trophy stacks. Items listed in global trophy level multiplier blacklist are exempt and keep their vanilla or YAML levelMultiplier behavior.", synchronizedSetting: true, 200); _trophyLevelMultiplierBlacklistEntry = plugin.BindConfigEntry("2 - Character", "global trophy level multiplier blacklist", "", "Comma, semicolon, or newline separated trophy item prefab names that should not use global trophy amount scaling. Blacklisted items follow the vanilla or YAML levelMultiplier value exactly. Example: TrophyEikthyr,TrophyDragonQueen", synchronizedSetting: true, 100); } internal static bool IsGlobalDropInStackEnabled() { ConfigEntry globalDropInStack = _globalDropInStack; if (globalDropInStack == null) { return false; } return globalDropInStack.Value == DropNSpawnPlugin.Toggle.On; } internal static bool IsMonsterInstantLootDropEnabled() { ConfigEntry monsterInstantLootDrop = _monsterInstantLootDrop; if (monsterInstantLootDrop == null) { return false; } return monsterInstantLootDrop.Value == DropNSpawnPlugin.Toggle.On; } internal static bool IsCalculateChanceLootSystemEnabled() { ConfigEntry characterLootSystem = _characterLootSystem; if (characterLootSystem == null) { return false; } return characterLootSystem.Value == CharacterLootSystem.CalculateChance; } internal static bool ShouldDisableLootScalingForCreatureLevelControl() { ConfigEntry disableCharacterLootScalingWhenCreatureLevelControlLoaded = _disableCharacterLootScalingWhenCreatureLevelControlLoaded; if (disableCharacterLootScalingWhenCreatureLevelControlLoaded != null && disableCharacterLootScalingWhenCreatureLevelControlLoaded.Value == DropNSpawnPlugin.Toggle.On) { return IsCreatureLevelControlLoaded(); } return false; } internal static int GetAdditionalLootChancePerStar(bool boss) { return Mathf.Clamp((!boss) ? (_additionalLootChancePerStarCreature?.Value ?? 50) : (_additionalLootChancePerStarBoss?.Value ?? 50), 0, 100); } internal static bool IsGlobalTrophyLevelMultiplierEnabled() { ConfigEntry globalTrophyLevelMultiplier = _globalTrophyLevelMultiplier; if (globalTrophyLevelMultiplier == null) { return false; } return globalTrophyLevelMultiplier.Value == DropNSpawnPlugin.Toggle.On; } internal static float GetOnePerPlayerNearbyRange() { return Mathf.Max(0f, _onePerPlayerNearbyRange?.Value ?? 32f); } internal static bool IsDropInStackBlacklisted(string? prefabName) { if (prefabName == null) { return false; } string text = prefabName.Trim(); if (text.Length == 0) { return false; } lock (DropInStackBlacklistLock) { EnsureDropInStackBlacklistCache(); return _dropInStackBlacklist.Contains(text); } } internal static bool IsTrophyLevelMultiplierBlacklisted(string? prefabName) { if (prefabName == null) { return false; } string text = prefabName.Trim(); if (text.Length == 0) { return false; } lock (TrophyLevelMultiplierBlacklistLock) { EnsureTrophyLevelMultiplierBlacklistCache(); return _trophyLevelMultiplierBlacklist.Contains(text); } } private static void EnsureDropInStackBlacklistCache() { string text = _dropInStackBlacklistEntry?.Value ?? ""; if (!string.Equals(_dropInStackBlacklistRaw, text, StringComparison.Ordinal)) { _dropInStackBlacklistRaw = text; _dropInStackBlacklist = ParseNameSet(text); } } private static void EnsureTrophyLevelMultiplierBlacklistCache() { string text = _trophyLevelMultiplierBlacklistEntry?.Value ?? ""; if (!string.Equals(_trophyLevelMultiplierBlacklistRaw, text, StringComparison.Ordinal)) { _trophyLevelMultiplierBlacklistRaw = text; _trophyLevelMultiplierBlacklist = ParseNameSet(text); } } private static HashSet ParseNameSet(string raw) { return (from value in raw.Split(new char[4] { ',', ';', '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries) select value.Trim() into value where value.Length > 0 select value).ToHashSet(StringComparer.OrdinalIgnoreCase); } private static bool IsCreatureLevelControlLoaded() { if (_isCreatureLevelControlLoaded.HasValue) { return _isCreatureLevelControlLoaded.Value; } _isCreatureLevelControlLoaded = Chainloader.PluginInfos.ContainsKey("org.bepinex.plugins.creaturelevelcontrol"); return _isCreatureLevelControlLoaded.Value; } } internal static class EventGlobalConfig { internal enum EventSchedulingMode { Vanilla, MultipleGlobal, MultiplePerPlayer } internal enum EventPlayerBaseDefault { Off, Away, Near, AwayAndNear } private const float DefaultMinimumDistanceBetweenEvents = 100f; private const float DefaultEventDurationMultiplier = 1f; private const float DefaultRandomEventChance = 20f; private const float DefaultRandomEventIntervalMinutes = 46f; private static ConfigEntry? _eventSchedulingMode; private static ConfigEntry? _defaultPlayerBase; private static ConfigEntry? _minimumDistanceBetweenEvents; private static ConfigEntry? _eventDurationMultiplier; private static ConfigEntry? _randomEventChance; private static ConfigEntry? _randomEventIntervalMinutes; internal static void Bind(DropNSpawnPlugin plugin) { //IL_00c4: Unknown result type (might be due to invalid IL or missing references) //IL_00d9: Expected O, but got Unknown //IL_012c: Unknown result type (might be due to invalid IL or missing references) //IL_0141: Expected O, but got Unknown //IL_0194: Unknown result type (might be due to invalid IL or missing references) //IL_01a9: Expected O, but got Unknown //IL_01fc: Unknown result type (might be due to invalid IL or missing references) //IL_0211: Expected O, but got Unknown Dispose(); _eventSchedulingMode = plugin.BindConfigEntry("3 - Events", "Event scheduling mode", EventSchedulingMode.Vanilla, "Vanilla allows one active random event and performs one server-wide check per interval. MultipleGlobal allows multiple active random events while keeping one server-wide check per interval. MultiplePerPlayer allows multiple active random events and performs one independent check per player per interval. Standalone events use the same global or per-player evaluation mode.", synchronizedSetting: true, 800); _eventSchedulingMode.SettingChanged += HandleRuntimeSettingChanged; _defaultPlayerBase = plugin.BindConfigEntry("3 - Events", "Default event player base", EventPlayerBaseDefault.Off, "Global default player-base condition for events that do not specify conditions.playerBase in YAML. Off keeps each event's baseline value. Away acts like playerBase: [away], Near acts like [near], and AwayAndNear acts like [away, near]. YAML values always win. Forced boss events do not use this random-event start filter.", synchronizedSetting: true, 650); _defaultPlayerBase.SettingChanged += HandleDefinitionSettingChanged; _minimumDistanceBetweenEvents = plugin.BindConfigEntry("3 - Events", "Minimum distance between events", 100f, new ConfigDescription("Minimum horizontal XZ distance between simultaneously active random event centers. A new event attempt inside this distance is ignored while the active event continues.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 10000f), Array.Empty()), synchronizedSetting: true, 600); _minimumDistanceBetweenEvents.SettingChanged += HandleRuntimeSettingChanged; _eventDurationMultiplier = plugin.BindConfigEntry("3 - Events", "Event duration multiplier", 1f, new ConfigDescription("Positive values scale event durations that do not explicitly set YAML settings[2]. 0.5 halves durations, 1 keeps them unchanged, and 2 doubles them. 0 is a master switch that disables every event whose effective duration after YAML is greater than 0; duration-0 events remain enabled.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 3f), Array.Empty()), synchronizedSetting: true, 550); _eventDurationMultiplier.SettingChanged += HandleDefinitionSettingChanged; _randomEventChance = plugin.BindConfigEntry("3 - Events", "Random event chance", 20f, new ConfigDescription("Chance from 0 to 100 to try starting a random event when the random event interval elapses. This maps to RandEventSystem.m_eventChance.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 100f), Array.Empty()), synchronizedSetting: true, 500); _randomEventChance.SettingChanged += HandleRuntimeSettingChanged; _randomEventIntervalMinutes = plugin.BindConfigEntry("3 - Events", "Random event interval", 46f, new ConfigDescription("Minutes between random event checks. 0 removes the cooldown and attempts a check every server FixedUpdate. This maps to RandEventSystem.m_eventIntervalMin.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 10000f), Array.Empty()), synchronizedSetting: true, 400); _randomEventIntervalMinutes.SettingChanged += HandleRuntimeSettingChanged; } internal static void Dispose() { if (_eventSchedulingMode != null) { _eventSchedulingMode.SettingChanged -= HandleRuntimeSettingChanged; } if (_defaultPlayerBase != null) { _defaultPlayerBase.SettingChanged -= HandleDefinitionSettingChanged; } if (_minimumDistanceBetweenEvents != null) { _minimumDistanceBetweenEvents.SettingChanged -= HandleRuntimeSettingChanged; } if (_eventDurationMultiplier != null) { _eventDurationMultiplier.SettingChanged -= HandleDefinitionSettingChanged; } if (_randomEventChance != null) { _randomEventChance.SettingChanged -= HandleRuntimeSettingChanged; } if (_randomEventIntervalMinutes != null) { _randomEventIntervalMinutes.SettingChanged -= HandleRuntimeSettingChanged; } _eventSchedulingMode = null; _defaultPlayerBase = null; _minimumDistanceBetweenEvents = null; _eventDurationMultiplier = null; _randomEventChance = null; _randomEventIntervalMinutes = null; } internal static bool IsMultipleEventsEnabled() { return (_eventSchedulingMode?.Value ?? EventSchedulingMode.Vanilla) != EventSchedulingMode.Vanilla; } internal static bool IsCheckPerPlayerEnabled() { return (_eventSchedulingMode?.Value ?? EventSchedulingMode.Vanilla) == EventSchedulingMode.MultiplePerPlayer; } internal static EventPlayerBaseDefault GetDefaultPlayerBase() { return _defaultPlayerBase?.Value ?? EventPlayerBaseDefault.Off; } internal static float GetMinimumDistanceBetweenEvents() { return Math.Max(0f, _minimumDistanceBetweenEvents?.Value ?? 100f); } internal static float GetEventDurationMultiplier() { return Clamp(_eventDurationMultiplier?.Value ?? 1f, 0f, 3f); } internal static float GetRandomEventChance() { return Clamp(_randomEventChance?.Value ?? 20f, 0f, 100f); } internal static float GetRandomEventIntervalMinutes() { return Math.Max(0f, _randomEventIntervalMinutes?.Value ?? 46f); } private static void HandleRuntimeSettingChanged(object? sender, EventArgs e) { EventManager.ApplyGlobalEventSettings(); } private static void HandleDefinitionSettingChanged(object? sender, EventArgs e) { EventManager.ReapplyEventDefinitions("event global config"); } private static float Clamp(float value, float min, float max) { return Math.Max(min, Math.Min(max, value)); } } internal static class SpawnerGlobalConfig { internal const int MinSpawnAreaMaxTotalSpawns = 0; internal const int MaxSpawnAreaMaxTotalSpawns = 1000; internal const int MinZeroCreatureSpawnerRespawnTimeMinutes = 0; internal const int MaxZeroCreatureSpawnerRespawnTimeMinutes = 60; private static ConfigEntry? _defaultSpawnAreaMaxTotalSpawns; private static ConfigEntry? _defaultZeroCreatureSpawnerRespawnTimeMinutes; internal static void Bind(DropNSpawnPlugin plugin) { //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Expected O, but got Unknown //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Expected O, but got Unknown Dispose(); _defaultSpawnAreaMaxTotalSpawns = plugin.BindConfigEntry("1 - General", "Default SpawnArea Max Total Spawns", 0, new ConfigDescription($"Default successful-spawn limit for every SpawnArea. 0 disables this option and leaves SpawnAreas unlimited. Values from 1 to {1000} make each SpawnArea destroy itself after that many successful spawns. Override per YAML entry with spawnArea.maxTotalSpawns.", (AcceptableValueBase)(object)new AcceptableValueRange(0, 1000), Array.Empty()), synchronizedSetting: true, 450); _defaultZeroCreatureSpawnerRespawnTimeMinutes = plugin.BindConfigEntry("1 - General", "Default zero CreatureSpawner respawn time minutes", 0, new ConfigDescription($"Default respawnTimeMinutes for CreatureSpawner components whose current value is 0. 0 disables this option. Values from 1 to {60} set only zero-respawn CreatureSpawners. Override per YAML entry with creatureSpawner.respawnTimeMinutes.", (AcceptableValueBase)(object)new AcceptableValueRange(0, 60), Array.Empty()), synchronizedSetting: true, 440); _defaultZeroCreatureSpawnerRespawnTimeMinutes.SettingChanged += HandleCreatureSpawnerRespawnSettingChanged; } internal static void Dispose() { if (_defaultZeroCreatureSpawnerRespawnTimeMinutes != null) { _defaultZeroCreatureSpawnerRespawnTimeMinutes.SettingChanged -= HandleCreatureSpawnerRespawnSettingChanged; } _defaultSpawnAreaMaxTotalSpawns = null; _defaultZeroCreatureSpawnerRespawnTimeMinutes = null; } internal static int GetDefaultSpawnAreaMaxTotalSpawns() { return ClampSpawnAreaMaxTotalSpawns(_defaultSpawnAreaMaxTotalSpawns?.Value ?? 0); } internal static int GetDefaultZeroCreatureSpawnerRespawnTimeMinutes() { return ClampZeroCreatureSpawnerRespawnTimeMinutes(_defaultZeroCreatureSpawnerRespawnTimeMinutes?.Value ?? 0); } internal static int ClampSpawnAreaMaxTotalSpawns(int value) { return Math.Max(0, Math.Min(1000, value)); } internal static int ClampZeroCreatureSpawnerRespawnTimeMinutes(int value) { return Math.Max(0, Math.Min(60, value)); } private static void HandleCreatureSpawnerRespawnSettingChanged(object? sender, EventArgs e) { SpawnerManager.ReapplyCreatureSpawnerGlobalDefaults(); } } internal static class EspSpawnSystemCompatibility { private readonly struct PendingRefresh { public SpawnSystem System { get; } public int SystemId { get; } public int Epoch { get; } public int ReadyFrame { get; } public PendingRefresh(SpawnSystem system, int systemId, int epoch, int readyFrame) { System = system; SystemId = systemId; Epoch = epoch; ReadyFrame = readyFrame; } } private const int RefreshFrameDelay = 1; private static readonly RingBufferQueue PendingRefreshes = new RingBufferQueue(); private static readonly HashSet PendingRefreshIds = new HashSet(); private static Harmony? _harmony; private static bool _hoverGuardPatchAttempted; private static bool _typesResolved; private static Type? _spawnSystemTextType; private static MethodInfo? _drawSpawnSystemsMethod; private static FieldInfo? _spawnSystemField; private static FieldInfo? _spawnDataField; private static bool _loggedCompatibilityFailure; internal static void Initialize(Harmony harmony) { if (_harmony == null) { _harmony = harmony; } ResolveTypes(); TryInstallHoverGuard(); } private static void TryInstallHoverGuard() { //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Expected O, but got Unknown if (_hoverGuardPatchAttempted || _harmony == null || _spawnSystemTextType == null) { return; } _hoverGuardPatchAttempted = true; MethodInfo methodInfo = AccessTools.DeclaredMethod(_spawnSystemTextType, "GetHoverText", Type.EmptyTypes, (Type[])null); MethodInfo methodInfo2 = AccessTools.DeclaredMethod(typeof(EspSpawnSystemCompatibility), "SpawnSystemTextGetHoverTextPrefix", (Type[])null, (Type[])null); if (_spawnSystemField == null || _spawnDataField == null || methodInfo == null || methodInfo2 == null) { LogCompatibilityFailureOnce("ESP detected, but SpawnSystemText compatibility members could not be resolved."); return; } try { _harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(methodInfo2), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } catch (Exception arg) { LogCompatibilityFailureOnce($"Failed to install the ESP SpawnSystem hover guard. {arg}"); } } internal static bool HasPendingRefreshes() { return PendingRefreshes.Count > 0; } internal static void ClearPendingRefreshes() { PendingRefreshes.Clear(); PendingRefreshIds.Clear(); } internal static void RemovePendingRefresh(int systemId) { PendingRefreshIds.Remove(systemId); } internal static void RequestRefresh(SpawnSystem? system, int epoch) { if (!((Object)(object)system == (Object)null) && !ShouldSkipRefresh()) { int instanceID = ((Object)system).GetInstanceID(); if (PendingRefreshIds.Add(instanceID)) { PendingRefreshes.Enqueue(new PendingRefresh(system, instanceID, epoch, Time.frameCount + 1)); } } } internal static bool TryProcessPendingRefresh(double deadline, int expectedEpoch) { while (PendingRefreshes.Count > 0) { if (Time.realtimeSinceStartupAsDouble >= deadline) { return false; } if (DropNSpawnPlugin.IsGameDataRefreshDeferred(DropNSpawnPlugin.ReloadDomain.SpawnSystem)) { return false; } if (!PendingRefreshes.TryPeek(out var item)) { continue; } if (item.ReadyFrame > Time.frameCount) { return false; } if (PendingRefreshes.TryDequeue(out item)) { PendingRefreshIds.Remove(item.SystemId); if (item.Epoch == expectedEpoch && !((Object)(object)item.System == (Object)null)) { RefreshMarkers(item.System); return true; } } } return false; } private static void RefreshMarkers(SpawnSystem system) { if (ShouldSkipRefresh() || !TryResolveHooks(out Type spawnSystemTextType, out MethodInfo drawSpawnSystemsMethod)) { return; } try { HashSet hashSet = new HashSet(); Component[] componentsInChildren = ((Component)system).GetComponentsInChildren(spawnSystemTextType, true); foreach (Component val in componentsInChildren) { if ((Object)(object)val != (Object)null && (Object)(object)val.gameObject != (Object)null) { ClearMarkerReferences(val); hashSet.Add(val.gameObject); } } foreach (GameObject item in hashSet) { item.SetActive(false); Object.Destroy((Object)(object)item); } drawSpawnSystemsMethod.Invoke(null, new object[1] { system }); } catch (Exception arg) { LogCompatibilityFailureOnce($"Failed to refresh ESP SpawnSystem markers after authoritative replace. {arg}"); } } private static void ClearMarkerReferences(Component marker) { try { _spawnSystemField?.SetValue(marker, null); _spawnDataField?.SetValue(marker, null); } catch (Exception arg) { LogCompatibilityFailureOnce($"Failed to clear stale ESP SpawnSystem marker references. {arg}"); } } private static bool SpawnSystemTextGetHoverTextPrefix(object __instance, ref string __result) { try { if ((Object)(object)ZNet.instance != (Object)null) { object? obj = _spawnSystemField?.GetValue(__instance); SpawnSystem val = (SpawnSystem)((obj is SpawnSystem) ? obj : null); if (val != null && (Object)(object)val != (Object)null && _spawnDataField?.GetValue(__instance) != null) { ZNetView component = ((Component)val).GetComponent(); if ((Object)(object)component != (Object)null && component.IsValid() && component.GetZDO() != null) { return true; } } } } catch { } __result = ""; return false; } private static bool ShouldSkipRefresh() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Invalid comparison between Unknown and I4 if ((int)SystemInfo.graphicsDeviceType == 4) { return true; } if ((Object)(object)ZNet.instance != (Object)null) { return ZNet.instance.IsDedicated(); } return false; } private static bool TryResolveHooks(out Type? spawnSystemTextType, out MethodInfo? drawSpawnSystemsMethod) { ResolveTypes(); TryInstallHoverGuard(); spawnSystemTextType = _spawnSystemTextType; drawSpawnSystemsMethod = _drawSpawnSystemsMethod; if (spawnSystemTextType != null) { return drawSpawnSystemsMethod != null; } return false; } private static void ResolveTypes() { if (!_typesResolved) { Type type = SafeTypeLookup.FindLoadedType("ESP.SpawnSystemText", "ESP"); if (!(type == null)) { _spawnSystemTextType = type; Type type2 = SafeTypeLookup.FindLoadedType("ESP.SpawnSystem_Awake", "ESP"); _drawSpawnSystemsMethod = ((type2 != null) ? AccessTools.Method(type2, "DrawSpawnSystems", (Type[])null, (Type[])null) : null); _spawnSystemField = ((_spawnSystemTextType != null) ? AccessTools.Field(_spawnSystemTextType, "spawnSystem") : null); _spawnDataField = ((_spawnSystemTextType != null) ? AccessTools.Field(_spawnSystemTextType, "spawnData") : null); _typesResolved = true; } } } private static void LogCompatibilityFailureOnce(string message) { if (!_loggedCompatibilityFailure) { _loggedCompatibilityFailure = true; DropNSpawnPlugin.DropNSpawnLogger.LogWarning((object)message); } } } internal static class SceneProximityQueries { internal static int CountPlayersInRangeXZ(Vector3 point, float range, bool livingPlayersOnly) { //IL_0011: 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) if (range <= 0f) { return 0; } if (DropNSpawnPlugin.IsRuntimeServer()) { return CountServerPlayersInRangeXZ(point, range * range, livingPlayersOnly); } int num = 0; float rangeSquared = range * range; foreach (Player allPlayer in Player.GetAllPlayers()) { if (IsPlayerInRangeXZ(allPlayer, point, rangeSquared, livingPlayersOnly)) { num++; } } return num; } private static int CountServerPlayersInRangeXZ(Vector3 point, float rangeSquared, bool livingPlayersOnly) { //IL_0002: 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) int num = 0; if (IsLocalServerPlayerInRangeXZ(point, rangeSquared, livingPlayersOnly)) { num++; } ZNet instance = ZNet.instance; List list = ((instance != null) ? instance.GetPeers() : null); if (list == null) { return num; } foreach (ZNetPeer item in list) { if (IsServerPeerInRangeXZ(item, point, rangeSquared, livingPlayersOnly)) { num++; } } return num; } private static bool IsPlayerInRangeXZ(Player? player, Vector3 point, float rangeSquared, bool livingPlayersOnly) { //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) if ((Object)(object)player != (Object)null && (Object)(object)((Component)player).gameObject != (Object)null && (!livingPlayersOnly || !((Character)player).IsDead())) { return IsWithinRangeXZ(((Component)player).transform.position, point, rangeSquared); } return false; } 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_000c: 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) if (peer == null || !peer.IsReady() || !IsWithinRangeXZ(peer.GetRefPos(), 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 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; } } 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(); string text2 = text; int num2 = num + 1; return new ValueTuple(item2: ParseNullableInt(text2.Substring(num2, text2.Length - num2).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(); string text2 = text; int num2 = num + 1; return new ValueTuple(item2: ParseNullableFloat(text2.Substring(num2, text2.Length - num2).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 InstanceReconcileQueue where TInstance : Object { private readonly RingBufferQueue _pendingItems = new RingBufferQueue(); private readonly HashSet _pendingInstanceIds = new HashSet(); private readonly Func _createQueuedItem; private readonly Func _getInstanceId; private readonly Func _getEpoch; private readonly Func _getInstance; public bool HasPendingWork => _pendingItems.Count > 0; public InstanceReconcileQueue(Func createQueuedItem, Func getInstanceId, Func getEpoch, Func getInstance) { _createQueuedItem = createQueuedItem; _getInstanceId = getInstanceId; _getEpoch = getEpoch; _getInstance = getInstance; } public bool TryQueue(TInstance? instance, int epoch) { if ((Object)(object)instance == (Object)null) { return false; } int instanceID = ((Object)instance).GetInstanceID(); if (!_pendingInstanceIds.Add(instanceID)) { return false; } _pendingItems.Enqueue(_createQueuedItem(instance, instanceID, epoch)); return true; } public bool TryDequeueCurrent(int epoch, out TInstance? instance, out int instanceId) { instance = default(TInstance); instanceId = 0; while (_pendingItems.Count > 0) { if (!_pendingItems.TryDequeue(out var item)) { continue; } instanceId = _getInstanceId(item); _pendingInstanceIds.Remove(instanceId); if (_getEpoch(item) == epoch) { instance = _getInstance(item); if (!((Object)(object)instance == (Object)null)) { return true; } } } instanceId = 0; return false; } public void Clear() { _pendingItems.Clear(); _pendingInstanceIds.Clear(); } } internal sealed class RingBufferQueue { private T[] _buffer; private int _head; private int _count; internal int Count => _count; internal RingBufferQueue(int capacity = 16) { _buffer = new T[Math.Max(4, capacity)]; } internal void Enqueue(T item) { EnsureCapacity(_count + 1); int num = (_head + _count) % _buffer.Length; _buffer[num] = item; _count++; } internal bool TryPeek(out T item) { if (_count == 0) { item = default(T); return false; } item = _buffer[_head]; return true; } internal bool TryDequeue(out T item) { if (_count == 0) { item = default(T); return false; } item = _buffer[_head]; _buffer[_head] = default(T); _head = (_head + 1) % _buffer.Length; _count--; if (_count == 0) { _head = 0; } return true; } internal void Clear() { if (_count > 0) { for (int i = 0; i < _count; i++) { _buffer[(_head + i) % _buffer.Length] = default(T); } } _head = 0; _count = 0; } private void EnsureCapacity(int required) { if (required > _buffer.Length) { int num; for (num = _buffer.Length * 2; num < required; num *= 2) { } T[] array = new T[num]; for (int i = 0; i < _count; i++) { array[i] = _buffer[(_head + i) % _buffer.Length]; } _buffer = array; _head = 0; } } } internal static class SafeTypeLookup { internal static Type? FindLoadedType(string fullTypeName, string? preferredAssemblySimpleName = null) { if (string.IsNullOrWhiteSpace(fullTypeName)) { return null; } Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); Assembly[] array; if (!string.IsNullOrWhiteSpace(preferredAssemblySimpleName)) { array = assemblies; foreach (Assembly assembly in array) { if (string.Equals(assembly.GetName().Name, preferredAssemblySimpleName, StringComparison.OrdinalIgnoreCase)) { Type type = TryGetType(assembly, fullTypeName); if (type != null) { return type; } } } } array = assemblies; for (int i = 0; i < array.Length; i++) { Type type2 = TryGetType(array[i], fullTypeName); if (type2 != null) { return type2; } } return null; } private static Type? TryGetType(Assembly assembly, string fullTypeName) { try { return assembly.GetType(fullTypeName, throwOnError: false, ignoreCase: false); } catch { return null; } } } internal sealed class TimeOfDayDefinition : IYamlConvertible { public List Values { get; set; } = new List(); internal bool HasValues() { return Values.Count > 0; } internal void Normalize() { Values = TimeOfDayFormatting.NormalizeValues(Values); } void IYamlConvertible.Read(IParser parser, Type expectedType, ObjectDeserializer nestedObjectDeserializer) { if (parser.TryConsume(out var @event)) { Values = TimeOfDayFormatting.NormalizeValues(new string[1] { @event.Value ?? "" }); return; } parser.Consume(); List list = new List(); SequenceEnd event2; while (!parser.Accept(out event2)) { list.Add(parser.Consume().Value ?? ""); } parser.Consume(); Values = TimeOfDayFormatting.NormalizeValues(list); } void IYamlConvertible.Write(IEmitter emitter, ObjectSerializer nestedObjectSerializer) { emitter.Emit(new SequenceStart(null, null, isImplicit: false, SequenceStyle.Flow)); foreach (string value in Values) { emitter.Emit(new YamlDotNet.Core.Events.Scalar(value)); } emitter.Emit(new SequenceEnd()); } } internal static class TimeOfDayFormatting { private static readonly HashSet InvalidTokenWarnings = new HashSet(StringComparer.OrdinalIgnoreCase); private static readonly string[] SupportedTokens = new string[2] { "day", "night" }; internal static List NormalizeValues(IEnumerable? values) { List list = new List(); if (values == null) { return list; } foreach (string value in values) { string text = NormalizeToken(value); if (text != null && !list.Contains(text, StringComparer.OrdinalIgnoreCase)) { list.Add(text); } } return list; } internal static TimeOfDayDefinition? FromSpawnFlags(bool allowDay, bool allowNight) { if (allowDay && allowNight) { return new TimeOfDayDefinition { Values = new List { "day", "night" } }; } if (allowDay) { return new TimeOfDayDefinition { Values = new List { "day" } }; } if (allowNight) { return new TimeOfDayDefinition { Values = new List { "night" } }; } return new TimeOfDayDefinition(); } internal static bool MatchesCurrentTime(TimeOfDayDefinition? definition) { if (definition == null) { return true; } if (!definition.HasValues()) { return false; } return definition.Values.Any(MatchesCurrentTimeToken); } internal static void GetBroadSpawnFlags(TimeOfDayDefinition? definition, out bool allowDay, out bool allowNight) { if (definition == null) { allowDay = true; allowNight = true; return; } if (!definition.HasValues()) { allowDay = false; allowNight = false; return; } allowDay = definition.Values.Any((string value) => string.Equals(value, "day", StringComparison.OrdinalIgnoreCase)); allowNight = definition.Values.Any((string value) => string.Equals(value, "night", StringComparison.OrdinalIgnoreCase)); } internal static void GetRuntimeSpawnFlags(TimeOfDayDefinition? definition, out bool allowDay, out bool allowNight) { GetBroadSpawnFlags(definition, out allowDay, out allowNight); if (definition != null && definition.HasValues()) { bool flag = MatchesCurrentTime(definition); if (EnvMan.IsDay() && !flag) { allowDay = false; } if (EnvMan.IsNight() && !flag) { allowNight = false; } } } internal static int GetCurrentRuntimePhaseMarker() { if ((Object)(object)EnvMan.instance == (Object)null) { return int.MinValue; } return (!EnvMan.IsNight()) ? 1 : 0; } internal static string FormatInlineList(TimeOfDayDefinition? definition, TimeOfDayDefinition? fallback = null) { List values = ((definition != null && definition.HasValues()) ? definition.Values : ((fallback != null && fallback.HasValues()) ? fallback.Values : new List())); return "[" + string.Join(", ", values) + "]"; } private static string? NormalizeToken(string? rawValue) { string text = (rawValue ?? "").Trim().ToLowerInvariant(); if (text.Length == 0) { return null; } if (text == "daytime") { text = "day"; } if (SupportedTokens.Contains(text, StringComparer.OrdinalIgnoreCase)) { return text; } WarnInvalidToken(text); return null; } private static bool MatchesCurrentTimeToken(string token) { if (!(token == "day")) { if (token == "night") { return EnvMan.IsNight(); } return false; } return EnvMan.IsDay(); } private static void WarnInvalidToken(string token) { if (InvalidTokenWarnings.Add(token)) { DropNSpawnPlugin.DropNSpawnLogger.LogWarning((object)("Unsupported timeOfDay token '" + token + "' was ignored. Supported values: day, night.")); } } } internal class SpawnSystemSpawnDefinition { [YamlMember(Order = 0)] public string? Name { get; set; } [YamlMember(Order = 1)] public bool? HuntPlayer { get; set; } [YamlMember(Order = 2)] public IntRangeDefinition? Level { get; set; } [YamlIgnore] public int? MinLevel { get; set; } [YamlIgnore] public int? MaxLevel { get; set; } [YamlMember(Order = 3)] public float? OverrideLevelUpChance { get; set; } [YamlMember(Order = 4)] public float? LevelUpMinCenterDistance { get; set; } [YamlMember(Order = 5)] public float? GroundOffset { get; set; } [YamlMember(Order = 6)] public float? GroundOffsetRandom { get; set; } [YamlMember(Order = 7)] public float? SpawnInterval { get; set; } [YamlMember(Order = 8)] public float? SpawnChance { get; set; } [YamlMember(Order = 9)] public FloatRangeDefinition? SpawnRadius { get; set; } [YamlIgnore] public float? SpawnRadiusMin { get; set; } [YamlIgnore] public float? SpawnRadiusMax { get; set; } [YamlMember(Order = 10)] public IntRangeDefinition? GroupSize { get; set; } [YamlIgnore] public int? GroupSizeMin { get; set; } [YamlIgnore] public int? GroupSizeMax { get; set; } [YamlMember(Order = 11)] public float? GroupRadius { get; set; } [YamlMember(Order = 12)] public float? NoSpawnRadius { get; set; } [YamlMember(Order = 13)] public int? MaxSpawned { get; set; } [YamlMember(Order = 14)] public FloatRangeDefinition? Tilt { get; set; } [YamlIgnore] public float? MinTilt { get; set; } [YamlIgnore] public float? MaxTilt { get; set; } [YamlMember(Order = 15)] public FloatRangeDefinition? Altitude { get; set; } [YamlIgnore] public float? MinAltitude { get; set; } [YamlIgnore] public float? MaxAltitude { get; set; } [YamlMember(Order = 16)] public FloatRangeDefinition? OceanDepth { get; set; } [YamlIgnore] public float? MinOceanDepth { get; set; } [YamlIgnore] public float? MaxOceanDepth { get; set; } [YamlMember(Order = 17)] public FloatRangeDefinition? DistanceFromCenter { get; set; } [YamlIgnore] public float? MinDistanceFromCenter { get; set; } [YamlIgnore] public float? MaxDistanceFromCenter { get; set; } [YamlMember(Order = 18)] public List? Biomes { get; set; } [YamlIgnore] public Biome? ResolvedBiomeMask { get; set; } [YamlMember(Order = 19)] public List? BiomeAreas { get; set; } [YamlMember(Order = 20)] public TimeOfDayDefinition? TimeOfDay { get; set; } [YamlMember(Order = 21)] public List? RequiredEnvironments { get; set; } [YamlMember(Order = 22)] public string? RequiredGlobalKey { get; set; } [YamlMember(Order = 23)] public bool? InLava { get; set; } [YamlMember(Order = 24)] public bool? InForest { get; set; } [YamlMember(Order = 25)] public bool? InsidePlayerBase { get; set; } [YamlMember(Order = 26)] public bool? CanSpawnCloseToPlayer { get; set; } [YamlMember(Order = 27)] public Dictionary? Fields { get; set; } [YamlMember(Order = 28)] public List? Objects { get; set; } [YamlMember(Order = 29)] public string? Data { get; set; } [YamlMember(Order = 30)] public string? Faction { get; set; } } internal sealed class CanonicalSpawnSystemEntry { [YamlMember(Order = 0)] public string? Prefab { get; set; } [YamlMember(Order = 1)] public bool Enabled { get; set; } = true; [YamlMember(Alias = "spawnSystem", Order = 2)] public SpawnSystemSpawnDefinition? SpawnSystem { get; set; } [YamlIgnore] public string RuleId { get; set; } = ""; [YamlIgnore] public string? SourcePath { get; set; } [YamlIgnore] public int? SourceLine { get; set; } [YamlIgnore] public int? SourceColumn { get; set; } [YamlIgnore] public string? ReferenceOwnerName { get; set; } } internal static class SpawnSystemCustomDataSupport { internal sealed class PreparedPayload { public DataEntry? CustomData { get; set; } public List? CustomObjects { get; set; } internal bool HasValues() { if (CustomData == null) { return (CustomObjects?.Count ?? 0) > 0; } return true; } } private static readonly Dictionary PayloadsBySpawnData = new Dictionary(); private static readonly int HashFaction = StringExtensionMethods.GetStableHashCode("faction"); internal static void ClearCustomData(SpawnSystem system) { if ((Object)(object)system == (Object)null) { return; } foreach (SpawnSystemList spawnList in system.m_spawnLists) { foreach (SpawnData spawner in spawnList.m_spawners) { PayloadsBySpawnData.Remove(spawner); } } } internal static void ApplyCustomData(SpawnData spawnData, CanonicalSpawnSystemEntry entry, string context) { if (spawnData != null) { ApplyPreparedPayload(spawnData, BuildPreparedPayload(spawnData, entry, context)); } } internal static PreparedPayload? BuildPreparedPayload(SpawnData spawnData, CanonicalSpawnSystemEntry entry, string context) { if (spawnData == null) { return null; } PreparedPayload preparedPayload = new PreparedPayload { CustomData = BuildCustomData(spawnData, entry, context), CustomObjects = BuildCustomObjects(entry.SpawnSystem?.Objects, context) }; if (!preparedPayload.HasValues()) { return null; } return preparedPayload; } internal static void ApplyPreparedPayload(SpawnData spawnData, PreparedPayload? payload) { if (spawnData != null) { if (payload == null || !payload.HasValues()) { PayloadsBySpawnData.Remove(spawnData); } else { PayloadsBySpawnData[spawnData] = payload; } } } internal static bool HasPreparedPayload(SpawnData? spawnData) { if (spawnData != null) { return PayloadsBySpawnData.ContainsKey(spawnData); } return false; } internal static void CopyPreparedPayload(SpawnData? source, SpawnData? target) { if (source != null && target != null) { if (PayloadsBySpawnData.TryGetValue(source, out PreparedPayload value)) { PayloadsBySpawnData[target] = value; } else { PayloadsBySpawnData.Remove(target); } } } internal static void InitializeSpawn(SpawnData critter, Vector3 spawnPoint) { //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) if (critter != null && PayloadsBySpawnData.TryGetValue(critter, out PreparedPayload value) && value.CustomData != null) { DataHelper.Init(critter.m_prefab, spawnPoint, Quaternion.identity, (Vector3?)null, value.CustomData); } } internal static void SpawnObjects(SpawnData critter, Vector3 spawnPoint) { //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) if (critter == null || !PayloadsBySpawnData.TryGetValue(critter, out PreparedPayload value) || value.CustomObjects == null || value.CustomObjects.Count == 0) { return; } foreach (BlueprintObject customObject in value.CustomObjects) { if (!(customObject.Chance < 1f) || !(Random.value > customObject.Chance)) { Spawn.BPO(customObject, spawnPoint, Quaternion.identity, Vector3.one, (Func)ObjectDataIdentity, (Func)ObjectPrefabIdentity, (List)null); } } } private static DataEntry? BuildCustomData(SpawnData spawnData, CanonicalSpawnSystemEntry entry, string context) { //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Expected O, but got Unknown //IL_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Expected O, but got Unknown SpawnSystemSpawnDefinition? spawnSystem = entry.SpawnSystem; DataEntry val = null; string text = spawnSystem?.Data; if (text != null && !string.IsNullOrWhiteSpace(text)) { val = DataHelper.Get(text, "DropNSpawn_spawnsystem:" + context); } DataEntry val2 = null; string text2 = spawnSystem?.Faction; if (text2 != null && !string.IsNullOrWhiteSpace(text2)) { if (val2 == null) { val2 = new DataEntry(); } DataEntry val3 = val2; if (val3.Strings == null) { val3.Strings = new Dictionary(); } val2.Strings[HashFaction] = DataValue.Simple(text2); } Dictionary dictionary = spawnSystem?.Fields; if (dictionary != null && dictionary.Count > 0) { if (val2 == null) { val2 = new DataEntry(); } ExpandWorldFieldOverrideSupport.AddFieldOverrides(val2, spawnData.m_prefab, dictionary); } return DataHelper.Merge(val, val2); } private static List? BuildCustomObjects(List? configuredObjects, string context) { if (configuredObjects == null || configuredObjects.Count == 0) { return null; } List list = new List(); foreach (string configuredObject in configuredObjects) { if (TryParseObjectDefinition(configuredObject, context, out BlueprintObject obj)) { list.Add(obj); } } if (list.Count != 0) { return list; } return null; } private static bool TryParseObjectDefinition(string? rawDefinition, string context, out BlueprintObject? obj) { //IL_00f8: Unknown result type (might be due to invalid IL or missing references) //IL_00fd: 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_010c: Unknown result type (might be due to invalid IL or missing references) //IL_0112: Expected O, but got Unknown obj = null; if (string.IsNullOrWhiteSpace(rawDefinition)) { return false; } string[] array = (from part in rawDefinition.Split(',') select part.Trim()).ToArray(); if (array.Length == 0 || array[0].Length == 0) { DropNSpawnPlugin.DropNSpawnLogger.LogWarning((object)("Entry '" + context + "' contains an invalid objects entry '" + rawDefinition + "'. Expected format: Prefab,posX,posZ,posY,chance,data.")); return false; } if (!TryParseObjectFloat(array, 1, 0f, context, rawDefinition, out var value) || !TryParseObjectFloat(array, 2, 0f, context, rawDefinition, out var value2) || !TryParseObjectFloat(array, 3, 0f, context, rawDefinition, out var value3) || !TryParseObjectFloat(array, 4, 1f, context, rawDefinition, out var value4)) { return false; } DataEntry val = DataHelper.Get((array.Length > 5) ? array[5] : "", "DropNSpawn_spawnsystem:" + context); obj = new BlueprintObject(array[0], new Vector3(value, value3, value2), Quaternion.identity, Vector3.one, val, value4, false); return true; } private static bool TryParseObjectFloat(string[] split, int index, float defaultValue, string context, string rawDefinition, out float value) { value = defaultValue; if (index >= split.Length || split[index].Length == 0) { return true; } if (float.TryParse(split[index], NumberStyles.Any, CultureInfo.InvariantCulture, out value)) { return true; } DropNSpawnPlugin.DropNSpawnLogger.LogWarning((object)("Entry '" + context + "' contains an invalid objects entry '" + rawDefinition + "'. '" + split[index] + "' is not a valid number.")); return false; } private static DataEntry? ObjectDataIdentity(DataEntry? data, string prefab) { return data; } private static string ObjectPrefabIdentity(string prefab) { return prefab; } } internal static class SpawnSystemManager { private readonly struct PendingLiveSystemAttach { public SpawnSystem System { get; } public int SystemId { get; } public int Epoch { get; } public int BuildVersion { get; } public CompiledSpawnSystemTable TargetTable { get; } public PendingLiveSystemAttach(SpawnSystem system, int systemId, int epoch, int buildVersion, CompiledSpawnSystemTable targetTable) { System = system; SystemId = systemId; Epoch = epoch; BuildVersion = buildVersion; TargetTable = targetTable; } } private sealed class SpawnSystemEntrySnapshot { public int ListIndex { get; set; } public int EntryIndex { get; set; } public SpawnData Data { get; set; } } private sealed class SpawnSystemSnapshot { public int SystemId { get; set; } public List Entries { get; } = new List(); } private sealed class PreparedSpawnSystemEntry { public CanonicalSpawnSystemEntry Entry { get; set; } public SpawnData Data { get; set; } public SpawnSystemCustomDataSupport.PreparedPayload? CustomDataPayload { get; set; } public TimeOfDayDefinition? RuntimeTimeOfDay { get; set; } } private sealed class PreparedSpawnSystemModel { public CanonicalSpawnSystemEntry Entry { get; set; } public string EntrySignature { get; set; } = ""; public string Context { get; set; } = ""; public TimeOfDayDefinition? RuntimeTimeOfDay { get; set; } } private sealed class FinalizedPreparedEntryCacheEntry { public int GameDataSignature { get; set; } public string EntrySignature { get; set; } = ""; public SpawnData Data { get; set; } public SpawnSystemCustomDataSupport.PreparedPayload? CustomDataPayload { get; set; } public TimeOfDayDefinition? RuntimeTimeOfDay { get; set; } } private sealed class PreparedEntriesBuildResult { public int BuildVersion { get; set; } public int GameDataSignature { get; set; } public bool DomainEnabled { get; set; } public string ApplyTargetSignature { get; set; } = ""; public bool QueueEspRefreshForLiveSystems { get; set; } public List Models { get; } = new List(); } private sealed class PendingPreparedEntriesBuildRequest { public int BuildVersion { get; set; } public int GameDataSignature { get; set; } public bool DomainEnabled { get; set; } public string ApplyTargetSignature { get; set; } = ""; public bool QueueEspRefreshForLiveSystems { get; set; } public List ConfigurationSnapshot { get; } = new List(); } private sealed class PendingCompiledTableBuildState { public int BuildVersion { get; set; } public int GameDataSignature { get; set; } public bool DomainEnabled { get; set; } public bool EagerClientSyncBuild { get; set; } public string ApplyTargetSignature { get; set; } = ""; public bool QueueEspRefreshForLiveSystems { get; set; } public string PreparedEntriesSignature { get; set; } = ""; public List Models { get; } = new List(); public int NextFinalizeIndex { get; set; } public List FinalizedEntries { get; } = new List(); public int NextCompiledEntryIndex { get; set; } public CompiledSpawnSystemTable? BuildingActiveTable { get; set; } public List? BuildingLiveEntries { get; set; } public CompiledSpawnSystemTable? PreviousActiveTable { get; set; } public CompiledSpawnSystemTable? PreviousVanillaTable { get; set; } } private sealed class CompiledSpawnSystemTable { public int GameDataSignature { get; set; } public string Signature { get; set; } = ""; public bool ReferenceSourceTrusted { get; set; } public int BaselineListCount { get; set; } public int BaselineRowCount { get; set; } public int BaselineContentHash { get; set; } public List Lists { get; } = new List(); public Dictionary RuntimeTimeOfDayBySpawnData { get; } = new Dictionary(); public Dictionary CustomPayloadsBySpawnData { get; } = new Dictionary(); } private struct SpawnListSummary { public int ListCount { get; set; } public int RowCount { get; set; } public int ContentHash { get; set; } } private sealed class PendingCompiledTableRetirement { public CompiledSpawnSystemTable Table { get; set; } public HashSet RemainingSystemIds { get; } = new HashSet(); } private sealed class SyncedSpawnSystemConfigurationState { public List Configuration { get; } = new List(); public string ConfigurationSignature { get; set; } = ""; public bool ConfigurationReady { get; set; } } private sealed class ReferenceCatalogSnapshot { public List LiveEntries { get; } = new List(); public string SourceSignature { get; set; } = ""; public bool HasAnyEntries => LiveEntries.Count > 0; } private sealed class SpawnSystemConfigurationRuntimeState { public List Configuration { get; set; } = new List(); public string ConfigurationSignature { get; set; } = ""; public bool ConfigurationReady { get; set; } public void Reset() { Configuration = new List(); ConfigurationSignature = ""; ConfigurationReady = false; } } private sealed class DeferredExpandWorldDataBiomeState { public bool WaitingForBiomeReady { get; private set; } public bool QueueEspRefreshForLiveSystems { get; private set; } public bool QueueLiveSystemAttach { get; private set; } public bool PublishSyncedConfiguration { get; private set; } public bool HasWork { get { if (!WaitingForBiomeReady) { return PublishSyncedConfiguration; } return true; } } public void Defer(bool queueEspRefreshForLiveSystems, bool queueLiveSystemAttach, bool publishSyncedConfiguration) { WaitingForBiomeReady = true; QueueEspRefreshForLiveSystems |= queueEspRefreshForLiveSystems; QueueLiveSystemAttach |= queueLiveSystemAttach; PublishSyncedConfiguration |= publishSyncedConfiguration; } public void Consume(out bool queueEspRefreshForLiveSystems, out bool queueLiveSystemAttach, out bool publishSyncedConfiguration) { queueEspRefreshForLiveSystems = QueueEspRefreshForLiveSystems; queueLiveSystemAttach = QueueLiveSystemAttach; publishSyncedConfiguration = PublishSyncedConfiguration; Clear(); } public void ClearPublishSyncedConfiguration() { PublishSyncedConfiguration = false; } public void Clear() { WaitingForBiomeReady = false; QueueEspRefreshForLiveSystems = false; QueueLiveSystemAttach = false; PublishSyncedConfiguration = false; } } private sealed class SpawnSystemBuildPipelineState { public int? PendingGameDataSignature { get; set; } public int PreparedEntriesBuildVersion { get; set; } public bool PreparedEntriesBuildInFlight { get; set; } public bool PreparedEntriesBuildWorkerRunning { get; set; } public PreparedEntriesBuildResult? CompletedPreparedEntriesBuildResult { get; set; } public PendingPreparedEntriesBuildRequest? PendingPreparedEntriesBuildRequest { get; set; } public PendingCompiledTableBuildState? PendingCompiledTableBuild { get; set; } public string PendingBuildTargetSignature { get; set; } = ""; public void ResetPreparedEntriesBuildPipeline(bool clearPendingTargetSignature) { PreparedEntriesBuildVersion++; PreparedEntriesBuildInFlight = false; CompletedPreparedEntriesBuildResult = null; PendingPreparedEntriesBuildRequest = null; PendingCompiledTableBuild = null; PendingGameDataSignature = null; if (clearPendingTargetSignature) { PendingBuildTargetSignature = ""; } } } private const int FinalizedPreparedEntriesPerStep = 1; private const int CompiledEntryBuildsPerStep = 8; private const string ReferenceAutoUpdateStateKey = "spawnsystem"; private const string SpawnIntervalMultiplierHeaderKey = "spawnIntervalMultiplier"; internal static readonly DomainModuleDefinition Module = new DomainModuleDefinition(new DomainModuleOptions { DomainKey = "spawnsystem", ReloadDomain = DropNSpawnPlugin.ReloadDomain.SpawnSystem, ManifestSettingKey = "spawnsystem_yaml", ManifestPriority = 96, ShouldReloadForPath = ShouldReloadForPath, Reload = ReloadConfiguration, InitializeRuntime = Initialize, OnGameDataReady = OnGameDataReady, HandleExpandWorldDataReady = HandleExpandWorldDataReady, DtoVersion = 3, TransportProfile = DomainTransportProfile.LargeWithArtifacts, DisplayName = "spawnsystem", CacheDirectoryName = "spawnsystem", ClientRequestPriority = 40, KeySelector = (CanonicalSpawnSystemEntry entry) => entry.RuleId, ApplyPayloadAction = ApplySyncedPayload, HasPendingReconcileWork = HasPendingReconcileWork, ProcessPendingReconcileStep = ProcessQueuedReconcileStep, BeforeClientManifestChanged = MarkSyncedPayloadPending, OnClientAuthorityCutover = EnterPendingSyncedPayloadState }); private static readonly object Sync = new object(); private static readonly IDeserializer Deserializer = new DeserializerBuilder().WithNamingConvention(CamelCaseNamingConvention.Instance).Build(); private static readonly ISerializer Serializer = new SerializerBuilder().WithNamingConvention(CamelCaseNamingConvention.Instance).ConfigureDefaultValuesHandling(DefaultValuesHandling.OmitNull | DefaultValuesHandling.OmitDefaults).Build(); private static readonly Dictionary SnapshotsBySystemId = new Dictionary(); private static readonly InvalidEntryDiagnostics InvalidEntryWarnings = new InvalidEntryDiagnostics(); private static readonly Dictionary TimeOfDayBySpawnData = new Dictionary(); private static readonly RingBufferQueue PendingLiveSystemAttaches = new RingBufferQueue(); private static readonly HashSet PendingLiveSystemAttachIds = new HashSet(); private static readonly HashSet PendingLiveSystemAttachEspRefreshIds = new HashSet(); private static readonly List PendingCompiledTableRetirements = new List(); private static readonly HashSet PreAttachedSpawnSystemIds = new HashSet(); private static readonly Dictionary LiveSystemsById = new Dictionary(); private static readonly List LiveSystemsSnapshot = new List(); private static readonly Dictionary FinalizedPreparedEntryCache = new Dictionary(StringComparer.Ordinal); private static readonly FieldInfo? SpawnSystemInstancesField = AccessTools.Field(typeof(SpawnSystem), "m_instances"); private static readonly string[] BiomeOutputOrder = new string[9] { "Meadows", "BlackForest", "Swamp", "Ocean", "Mountain", "Plains", "Mistlands", "AshLands", "DeepNorth" }; private static readonly Dictionary BiomeOutputOrderLookup = BuildBiomeOutputOrderLookup(); private static readonly SpawnSystemConfigurationRuntimeState RuntimeState = new SpawnSystemConfigurationRuntimeState(); private static readonly DeferredExpandWorldDataBiomeState DeferredBiomeState = new DeferredExpandWorldDataBiomeState(); private static readonly SpawnSystemBuildPipelineState BuildPipelineState = new SpawnSystemBuildPipelineState(); private static string _lastFailedConfigurationPayload = ""; private static bool _initialized; private static int? _lastCompletedGameDataSignature; private static string _lastAppliedConfigurationSignature = ""; private static string _lastAppliedPreparedEntriesSignature = ""; private static int? _lastAppliedGameDataSignature; private static int? _lastCommittedAuthorityEpoch; private static bool? _lastAppliedDomainEnabled; private static int _reconcileQueueEpoch; private static int _requiredGlobalKeyEvaluationDepth; private static int? _lastRuntimeTimeOfDayPhaseMarker; private static int _lastRuntimeTimeOfDayRefreshFrame = -1; private static SpawnSystemSnapshot? _templateSnapshot; private static bool _hasRuntimeTimeOfDayOverrides; private static CompiledSpawnSystemTable? _activeCompiledTable; private static CompiledSpawnSystemTable? _vanillaCompiledTable; private static GameObject? _managedSpawnListHost; private static GameObject? _attachedSpawnListHost; private static bool _liveSystemsSnapshotDirty = true; private static bool _liveSystemsBootstrapAttempted; private static int? _liveSystemsRegistrySceneInstanceId; private static string _lastAppliedBuildTargetSignature = ""; private static HashSet? _capturedStrictValidationWarnings; private static bool _forceApplyAfterSyncedCommit; private static int _cachedGameDataSignatureFrame = -1; private static int _cachedGameDataSignatureValue; private static readonly List _cachedConfiguredSpawnSystemResolutionKeys = new List(); private static string _cachedConfiguredSpawnSystemResolutionKeysSignature = ""; private static int _cachedConfiguredSpawnSystemResolutionKeysSceneStamp = int.MinValue; private static readonly DomainConfigurationRuntime ConfigurationRuntime = new DomainConfigurationRuntime(new DomainLoadHooks(ParseLocalConfigurationDocuments, BuildSyncedConfigurationState, CommitConfigurationState, RejectLocalConfigurationPayload, (SyncedSpawnSystemConfigurationState state) => state.Configuration.Count, LogPartiallyAcceptedLocalConfiguration, LogLocalConfigurationLoaded, OnSourceOfTruthPayloadUnchanged, PublishSyncedConfigurationOrDeferLocked, CanStrictlyValidateLocalConfigurationNow, StrictValidateLocalConfiguration), new DomainSyncHooks(delegate(out List configuration, out string payloadToken) { return ConfigurationDomainHost.TryGetSyncedEntries(Descriptor, out configuration, out payloadToken); }, (string payloadToken) => ConfigurationDomainHost.ShouldSkipSyncedPayload(LoadState, payloadToken, RuntimeState.ConfigurationReady), BuildSyncedConfigurationState, CommitConfigurationState, (SyncedSpawnSystemConfigurationState state) => state.Configuration.Count, "ServerSync:DropNSpawnSpawnSystem", MarkSyncedPayloadPending, LogSyncedSpawnSystemConfigurationLoaded, LogSyncedSpawnSystemConfigurationFailure)); private static readonly Biome LowTierBiomeGlobalKeySpawnSystemFilter = (Biome)31; internal static DomainDescriptor Descriptor => Module.DescriptorTyped; internal static DomainTransportMetadata TransportMetadata => Module.TransportMetadataTyped; private static DomainLoadState LoadState => ConfigurationRuntime.LoadState; private static string ReferenceConfigurationPath => Path.Combine(DropNSpawnPlugin.YamlConfigDirectoryPath, PluginSettingsFacade.GetYamlDomainFilePrefix("spawnsystem") + ".reference.yml"); private static string PrimaryOverrideConfigurationPathYml => Path.Combine(DropNSpawnPlugin.YamlConfigDirectoryPath, PluginSettingsFacade.GetYamlDomainFilePrefix("spawnsystem") + ".yml"); private static string PrimaryOverrideConfigurationPathYaml => Path.Combine(DropNSpawnPlugin.YamlConfigDirectoryPath, PluginSettingsFacade.GetYamlDomainFilePrefix("spawnsystem") + ".yaml"); private static string FullScaffoldConfigurationPath => Path.Combine(DropNSpawnPlugin.YamlConfigDirectoryPath, PluginSettingsFacade.GetYamlDomainFilePrefix("spawnsystem") + ".full.yml"); internal static bool ShouldReloadForPath(string? path) { if (PluginSettingsFacade.IsEligibleOverrideConfigurationPath(path)) { return IsOverrideConfigurationFileName(Path.GetFileName(path ?? "")); } return false; } private static bool ShouldApplyLocally() { return PluginSettingsFacade.IsSpawnSystemDomainEnabled(); } internal static void MarkSyncedPayloadPending() { lock (Sync) { ConfigurationRuntime.MarkSyncedPayloadPending(DropNSpawnPlugin.IsSourceOfTruth, delegate { ClearQueuedReconcileState(); ResetPreparedEntriesBuildPipelineLocked(clearPendingTargetSignature: true); RuntimeState.ConfigurationReady = false; _forceApplyAfterSyncedCommit = false; }); } } internal static void EnterPendingSyncedPayloadState() { lock (Sync) { ConfigurationRuntime.EnterPendingSyncedPayloadState(DropNSpawnPlugin.IsSourceOfTruth, ResetLoadedConfigurationState, delegate { RuntimeState.ConfigurationSignature = ""; _lastFailedConfigurationPayload = ""; _lastCommittedAuthorityEpoch = null; _lastAppliedConfigurationSignature = ""; _lastAppliedPreparedEntriesSignature = ""; _lastAppliedBuildTargetSignature = ""; _lastAppliedGameDataSignature = null; _lastAppliedDomainEnabled = null; _forceApplyAfterSyncedCommit = false; RestoreBaselineWhileWaitingForSyncedPayload(); }); } } private static bool CanRetainCurrentCompiledTableWhilePending(int gameDataSignature) { if (ShouldApplyLocally() && !DropNSpawnPlugin.IsSourceOfTruth && !RuntimeState.ConfigurationReady && gameDataSignature != 0 && _activeCompiledTable != null && _activeCompiledTable.Lists.Count > 0 && _lastAppliedGameDataSignature == gameDataSignature) { return _lastCommittedAuthorityEpoch == NetworkPayloadSyncSupport.CurrentAuthorityEpoch; } return false; } private static void RestoreBaselineWhileWaitingForSyncedPayload() { int num = ComputeGameDataSignature(); if (num == 0) { _activeCompiledTable = null; return; } CompiledSpawnSystemTable? selectedCompiledTableForCurrentState = GetSelectedCompiledTableForCurrentState(); List liveSystems = GetLiveSystems(); EnsureVanillaCompiledTableCurrentLocked(num); _activeCompiledTable = null; CompiledSpawnSystemTable selectedCompiledTableForCurrentState2 = GetSelectedCompiledTableForCurrentState(); QueueLiveSystemAttachForTable(selectedCompiledTableForCurrentState2, BuildPipelineState.PreparedEntriesBuildVersion, queueEspRefresh: true, liveSystems); RetireCompiledTableAfterMigrationLocked(selectedCompiledTableForCurrentState, selectedCompiledTableForCurrentState2, liveSystems); } internal static void Initialize() { lock (Sync) { if (!_initialized) { LoadConfiguration(); _initialized = true; } } } internal static void ReloadConfiguration() { lock (Sync) { LoadConfiguration(); ApplyIfReady(queueEspRefreshForLiveSystems: true, queueLiveSystemAttach: true); } } internal static bool HandleExpandWorldDataReady() { lock (Sync) { if (!DropNSpawnPlugin.IsSourceOfTruth) { return false; } string lastLoadedPayload = LoadState.LastLoadedPayload; string configurationSignature = RuntimeState.ConfigurationSignature; bool num = LoadState.PendingStrictPayload.Length > 0; bool hasWork = DeferredBiomeState.HasWork; LoadConfiguration(); ApplyIfReady(queueEspRefreshForLiveSystems: true, queueLiveSystemAttach: true); return num || hasWork || !string.Equals(lastLoadedPayload, LoadState.LastLoadedPayload, StringComparison.Ordinal) || !string.Equals(configurationSignature, RuntimeState.ConfigurationSignature, StringComparison.Ordinal); } } internal static void ApplySyncedPayload() { lock (Sync) { ConfigurationRuntime.ApplySyncedPayload(delegate { _forceApplyAfterSyncedCommit = true; ApplyIfReady(queueEspRefreshForLiveSystems: true, queueLiveSystemAttach: true); }); } } internal static void OnGameDataReady(string source) { lock (Sync) { if (!_initialized) { Initialize(); } EnsureLiveSystemRegistrySessionLocked(); int num = ComputeGameDataSignature(); if (_lastCompletedGameDataSignature != num && BuildPipelineState.PendingGameDataSignature != num) { BuildPipelineState.PendingGameDataSignature = num; ClearQueuedReconcileState(); RefreshSnapshots(); if (DropNSpawnPlugin.IsSourceOfTruth) { HandleSourceOfTruthGameDataReady(); } ApplyIfReady(queueEspRefreshForLiveSystems: false, queueLiveSystemAttach: true); } } } internal static bool ProcessQueuedReconcileStep(double deadline) { lock (Sync) { return TryProcessQueuedReconcileWorkLocked(deadline); } } private static bool TryProcessQueuedReconcileWorkLocked(double deadline) { if (Time.realtimeSinceStartupAsDouble >= deadline) { return false; } if (DropNSpawnPlugin.IsGameDataRefreshDeferred(DropNSpawnPlugin.ReloadDomain.SpawnSystem)) { return false; } if (TryActivateCompletedPreparedEntriesBuildLocked()) { return true; } if (TryProcessDeferredExpandWorldDataBiomeReadyLocked()) { return true; } if (TryProcessPendingCompiledTableBuild(deadline)) { return true; } if (TryProcessPendingLiveSystemAttach(deadline)) { return true; } if (EspSpawnSystemCompatibility.TryProcessPendingRefresh(deadline, _reconcileQueueEpoch)) { return true; } return false; } internal static bool HasPendingReconcileWork() { lock (Sync) { return HasPendingReconcileWorkLocked(); } } private static bool HasPendingReconcileWorkLocked() { if (BuildPipelineState.CompletedPreparedEntriesBuildResult == null && BuildPipelineState.PendingCompiledTableBuild == null && !DeferredBiomeState.HasWork && PendingLiveSystemAttaches.Count <= 0) { return EspSpawnSystemCompatibility.HasPendingRefreshes(); } return true; } private static void HandleSourceOfTruthGameDataReady() { EnsurePrimaryOverrideConfigurationFileExists(); LoadConfiguration(); EnsureReferenceArtifactsUpToDate(); } internal static void RefreshRuntimeTimeOfDayState() { if (!_hasRuntimeTimeOfDayOverrides) { return; } int frameCount = Time.frameCount; if (_lastRuntimeTimeOfDayRefreshFrame == frameCount) { return; } lock (Sync) { if (!ShouldApplyLocally()) { _lastRuntimeTimeOfDayPhaseMarker = null; _lastRuntimeTimeOfDayRefreshFrame = -1; } else { if (!_hasRuntimeTimeOfDayOverrides || _lastRuntimeTimeOfDayRefreshFrame == frameCount) { return; } int currentRuntimePhaseMarker = TimeOfDayFormatting.GetCurrentRuntimePhaseMarker(); if (_lastRuntimeTimeOfDayPhaseMarker.HasValue && _lastRuntimeTimeOfDayPhaseMarker.Value == currentRuntimePhaseMarker) { _lastRuntimeTimeOfDayRefreshFrame = frameCount; return; } foreach (var (val2, definition) in TimeOfDayBySpawnData) { if (val2 != null) { TimeOfDayFormatting.GetRuntimeSpawnFlags(definition, out var allowDay, out var allowNight); val2.m_spawnAtDay = allowDay; val2.m_spawnAtNight = allowNight; } } _lastRuntimeTimeOfDayPhaseMarker = currentRuntimePhaseMarker; _lastRuntimeTimeOfDayRefreshFrame = frameCount; } } } private static bool EnsurePrimaryOverrideConfigurationFileExists() { if (DomainConfigurationFileSupport.HasAnyOverrideConfigurationFile("spawnsystem", PrimaryOverrideConfigurationPathYml, PrimaryOverrideConfigurationPathYaml)) { return false; } SpawnSystemSnapshot templateSnapshot = GetTemplateSnapshot(); if (templateSnapshot == null || templateSnapshot.Entries.Count == 0) { return false; } GeneratedArtifactWriter.WriteTextAlways(PrimaryOverrideConfigurationPathYml, BuildCompressedPrimaryOverrideConfigurationDocument(templateSnapshot), "Created spawnsystem override configuration at " + PrimaryOverrideConfigurationPathYml + "."); return true; } private static void LoadConfiguration() { if (DropNSpawnPlugin.IsSourceOfTruth) { ConfigurationRuntime.ReloadSourceOfTruth(EnumerateOverrideConfigurationPaths().ToList()); } else { ConfigurationRuntime.ReloadSynced(); } } private static void ResetLoadedConfigurationState() { ClearQueuedReconcileState(); ResetPreparedEntriesBuildPipelineLocked(clearPendingTargetSignature: true); InvalidEntryWarnings.Clear(); RuntimeState.Reset(); DeferredBiomeState.Clear(); } private static List CloneAndNormalizeConfigurationEntries(List? configuration, string sourceName) { List list = NetworkPayloadSyncSupport.CloneEntries(Descriptor, configuration); foreach (CanonicalSpawnSystemEntry item in list) { item.SourcePath = (string.IsNullOrWhiteSpace(item.SourcePath) ? sourceName : item.SourcePath); } if (list.Count > 0) { NormalizeConfiguration(list); } return list; } private static SyncedSpawnSystemConfigurationState BuildSyncedConfigurationState(List configuration, string sourceName) { using (BeginInvalidEntryWarningSuppressionForSyncedClientBuild(sourceName)) { SyncedSpawnSystemConfigurationState syncedSpawnSystemConfigurationState = new SyncedSpawnSystemConfigurationState { ConfigurationReady = true }; foreach (CanonicalSpawnSystemEntry item in CloneAndNormalizeConfigurationEntries(configuration, sourceName)) { syncedSpawnSystemConfigurationState.Configuration.Add(item); } syncedSpawnSystemConfigurationState.ConfigurationSignature = NetworkPayloadSyncSupport.ComputeSpawnSystemConfigurationSignature(syncedSpawnSystemConfigurationState.Configuration); return syncedSpawnSystemConfigurationState; } } private static LocalLoadResult ParseLocalConfigurationDocuments(List documents) { return ConfigurationLoadSupport.ParseLocalConfigurationDocuments(documents, ParseConfiguration, (List configuration, string path, List _) => CloneAndNormalizeConfigurationEntries(configuration, path), FormatYamlExceptionLocation, "Spawnsystem authoritative YAML must use a root list with an optional first '- spawnIntervalMultiplier: 2.0' header followed by entries like '- prefab: Fox'."); } private static bool CanStrictlyValidateLocalConfigurationNow(IEnumerable configuration) { if ((Object)(object)ZNetScene.instance != (Object)null && (Object)(object)ObjectDB.instance != (Object)null) { return !ShouldDeferForExpandWorldDataBiomeReady(configuration); } return false; } private static List FilterStrictlyValidatedLocalConfiguration(IEnumerable configuration, out List warnings) { //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Expected O, but got Unknown warnings = new List(); List list = new List(); HashSet hashSet = (_capturedStrictValidationWarnings = new HashSet(StringComparer.OrdinalIgnoreCase)); try { List list2 = configuration?.ToList() ?? new List(); for (int i = 0; i < list2.Count; i++) { CanonicalSpawnSystemEntry canonicalSpawnSystemEntry = list2[i]; string text = CreateConfigurationContext(i, canonicalSpawnSystemEntry); int count = hashSet.Count; try { SpawnData val = new SpawnData(); if (!ApplyEntry(val, canonicalSpawnSystemEntry, text, applyCustomData: false)) { if (hashSet.Count == count) { warnings.Add("Entry '" + text + "' failed strict spawnsystem validation and was skipped."); } } else { SpawnSystemCustomDataSupport.BuildPreparedPayload(val, canonicalSpawnSystemEntry, text); list.Add(canonicalSpawnSystemEntry); } } catch (Exception ex) { warnings.Add("Entry '" + text + "' failed strict spawnsystem validation and was skipped. " + ex.Message); } } } finally { _capturedStrictValidationWarnings = null; } foreach (string item in hashSet) { warnings.Add(item); } return list; } private static StrictValidationResult StrictValidateLocalConfiguration(List configuration) { RefreshResolvedBiomeMasksForConfiguration(configuration); List warnings; List entries = FilterStrictlyValidatedLocalConfiguration(configuration, out warnings); return new StrictValidationResult { Entries = entries, Warnings = warnings }; } private static void LogPartiallyAcceptedLocalConfiguration(int totalEntries, int acceptedEntries, IEnumerable warnings) { int num = Math.Max(0, totalEntries - acceptedEntries); DropNSpawnPlugin.DropNSpawnLogger.LogWarning((object)("Skipped " + num.ToString(CultureInfo.InvariantCulture) + " invalid spawnsystem entr" + ((num == 1) ? "y" : "ies") + " and kept " + acceptedEntries.ToString(CultureInfo.InvariantCulture) + " valid entr" + ((acceptedEntries == 1) ? "y" : "ies") + ".")); foreach (string item in warnings.Where((string message) => !string.IsNullOrWhiteSpace(message)).Distinct(StringComparer.OrdinalIgnoreCase)) { DropNSpawnPlugin.DropNSpawnLogger.LogWarning((object)item); } } private static string BuildStrictLocalValidationEnvironmentKey() { int num = ComputeGameDataSignature(); string text = (BiomeResolutionSupport.IsExpandWorldDataReadyOrUnavailable() ? "ready" : "waiting"); return num.ToString(CultureInfo.InvariantCulture) + "|" + text; } private static void RejectLocalConfigurationPayload(string payload, IEnumerable errors) { ConfigurationDomainHost.RejectLocalConfigurationPayload(LoadState, payload, errors, "spawnsystem", BuildStrictLocalValidationEnvironmentKey()); } private static void CommitConfigurationState(SyncedSpawnSystemConfigurationState state, string payloadToken) { ResetLoadedConfigurationState(); RuntimeState.Configuration = state.Configuration; RuntimeState.ConfigurationReady = state.ConfigurationReady; RuntimeState.ConfigurationSignature = state.ConfigurationSignature; _lastCommittedAuthorityEpoch = (DropNSpawnPlugin.IsSourceOfTruth ? ((int?)null) : new int?(NetworkPayloadSyncSupport.CurrentAuthorityEpoch)); LoadState.LastLoadedPayload = payloadToken; _lastFailedConfigurationPayload = ""; LoadState.LastRejectedPayload = ""; LoadState.LastRejectedValidationKey = ""; LoadState.PendingStrictPayload = ""; } private static void LogLocalConfigurationLoaded(int acceptedEntryCount, int loadedFileCount) { DropNSpawnPlugin.DropNSpawnLogger.LogInfo((object)$"Loaded {acceptedEntryCount} spawnsystem configuration(s) from {loadedFileCount} override file(s)."); } private static void OnSourceOfTruthPayloadUnchanged() { RefreshResolvedBiomeMasksForSourceOfTruthConfigurationLocked(); RuntimeState.ConfigurationSignature = NetworkPayloadSyncSupport.ComputeSpawnSystemConfigurationSignature(RuntimeState.Configuration); if (!NetworkPayloadSyncSupport.IsPayloadCurrent(Descriptor, RuntimeState.ConfigurationSignature) || DeferredBiomeState.HasWork) { PublishSyncedConfigurationOrDeferLocked(); } } private static void LogSyncedSpawnSystemConfigurationLoaded(string payloadToken, int acceptedEntryCount) { DropNSpawnPlugin.DropNSpawnLogger.LogInfo((object)$"Loaded {acceptedEntryCount} synchronized spawnsystem configuration(s) from the server."); } private static void LogSyncedSpawnSystemConfigurationFailure(string payloadToken, Exception ex) { if (!string.Equals(_lastFailedConfigurationPayload, payloadToken, StringComparison.Ordinal)) { DropNSpawnPlugin.DropNSpawnLogger.LogError((object)$"Failed to deserialize synchronized spawnsystem payload DTO. {ex}"); _lastFailedConfigurationPayload = payloadToken; } } private static void ClearQueuedReconcileState() { _reconcileQueueEpoch++; PendingLiveSystemAttaches.Clear(); PendingLiveSystemAttachIds.Clear(); PendingLiveSystemAttachEspRefreshIds.Clear(); EspSpawnSystemCompatibility.ClearPendingRefreshes(); PreAttachedSpawnSystemIds.Clear(); } private static void ResetPreparedEntriesBuildPipelineLocked(bool clearPendingTargetSignature) { BuildPipelineState.ResetPreparedEntriesBuildPipeline(clearPendingTargetSignature); } private static IEnumerable EnumerateOverrideConfigurationPaths() { return DomainConfigurationFileSupport.EnumerateOverrideConfigurationPaths("spawnsystem", PrimaryOverrideConfigurationPathYml, PrimaryOverrideConfigurationPathYaml, usePreferredPrimaryPath: true, WarnInvalidEntry); } private static ConfigurationLoadSupport.ParsedLocalConfiguration ParseConfiguration(string yaml, string? sourcePath) { ConfigurationLoadSupport.ParsedLocalConfiguration parsedLocalConfiguration = new ConfigurationLoadSupport.ParsedLocalConfiguration(); if (string.IsNullOrWhiteSpace(yaml)) { return parsedLocalConfiguration; } using StringReader input = new StringReader(yaml); YamlStream yamlStream = new YamlStream(); yamlStream.Load(input); if (yamlStream.Documents.Count == 0) { return parsedLocalConfiguration; } if (!(yamlStream.Documents[0].RootNode is YamlSequenceNode yamlSequenceNode)) { throw new YamlException(yamlStream.Documents[0].RootNode.Start, yamlStream.Documents[0].RootNode.End, "Spawnsystem authoritative YAML root must be a sequence."); } float num = 1f; YamlMappingNode yamlMappingNode = null; for (int i = 0; i < yamlSequenceNode.Children.Count; i++) { YamlNode yamlNode = yamlSequenceNode.Children[i]; if (!(yamlNode is YamlMappingNode yamlMappingNode2)) { parsedLocalConfiguration.Warnings.Add("Skipped spawnsystem YAML node at " + FormatYamlNodeLocation(sourcePath, yamlNode.Start) + ". Expected a list item object like '- prefab: Fox' but found " + DescribeYamlNode(yamlNode) + "."); continue; } if (TryGetSpawnIntervalMultiplierHeaderValue(yamlMappingNode2, out YamlNode valueNode)) { if (i != 0) { throw new YamlException(yamlMappingNode2.Start, yamlMappingNode2.End, "'spawnIntervalMultiplier' must be the first item in a spawnsystem override file."); } if (yamlMappingNode2.Children.Count != 1) { throw new YamlException(yamlMappingNode2.Start, yamlMappingNode2.End, "The 'spawnIntervalMultiplier' header cannot contain prefab or other entry fields."); } num = ParseSpawnIntervalMultiplier(valueNode); yamlMappingNode = yamlMappingNode2; continue; } if (i == 0 && yamlMappingNode2.Children.Count == 1 && yamlMappingNode2.Children.First().Key is YamlScalarNode yamlScalarNode && !IsSpawnSystemEntryTopLevelKey(yamlScalarNode.Value)) { throw new YamlException(yamlMappingNode2.Start, yamlMappingNode2.End, "Unrecognized first spawnsystem list item '" + yamlScalarNode.Value + "'. Use '- spawnIntervalMultiplier: 2.0' or start with a prefab entry."); } checked { try { string input2 = SerializeYamlNode(yamlMappingNode2); CanonicalSpawnSystemEntry canonicalSpawnSystemEntry = Deserializer.Deserialize(input2) ?? new CanonicalSpawnSystemEntry(); canonicalSpawnSystemEntry.SourceLine = (int)yamlMappingNode2.Start.Line; canonicalSpawnSystemEntry.SourceColumn = (int)yamlMappingNode2.Start.Column; parsedLocalConfiguration.Configuration.Add(canonicalSpawnSystemEntry); } catch (Exception ex) { parsedLocalConfiguration.Warnings.Add("Skipped invalid spawnsystem entry at " + FormatYamlNodeLocation(sourcePath, yamlMappingNode2.Start) + ". " + FormatEntryParseFailure(ex)); } } } if (yamlMappingNode != null && num != 1f) { MaterializeSpawnIntervalMultiplier(parsedLocalConfiguration.Configuration, num, yamlMappingNode, sourcePath); } return parsedLocalConfiguration; } private static bool TryGetSpawnIntervalMultiplierHeaderValue(YamlMappingNode mappingNode, out YamlNode? valueNode) { foreach (KeyValuePair child in mappingNode.Children) { if (child.Key is YamlScalarNode yamlScalarNode && string.Equals(yamlScalarNode.Value, "spawnIntervalMultiplier", StringComparison.OrdinalIgnoreCase)) { valueNode = child.Value; return true; } } valueNode = null; return false; } private static bool IsSpawnSystemEntryTopLevelKey(string? key) { if (!string.Equals(key, "prefab", StringComparison.OrdinalIgnoreCase) && !string.Equals(key, "enabled", StringComparison.OrdinalIgnoreCase)) { return string.Equals(key, "spawnSystem", StringComparison.OrdinalIgnoreCase); } return true; } private static float ParseSpawnIntervalMultiplier(YamlNode valueNode) { float? num; try { num = Deserializer.Deserialize(SerializeYamlNode(valueNode)); } catch (Exception ex) { throw new YamlException(valueNode.Start, valueNode.End, "'spawnIntervalMultiplier' must be a finite number greater than zero. " + FormatEntryParseFailure(ex)); } if (!num.HasValue || float.IsNaN(num.Value) || float.IsInfinity(num.Value) || num.Value <= 0f) { throw new YamlException(valueNode.Start, valueNode.End, "'spawnIntervalMultiplier' must be a finite number greater than zero."); } return num.Value; } private static void MaterializeSpawnIntervalMultiplier(IEnumerable configuration, float multiplier, YamlMappingNode header, string? sourcePath) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) float spawnInterval = new SpawnData().m_spawnInterval; foreach (CanonicalSpawnSystemEntry item in configuration) { float num = (item.SpawnSystem?.SpawnInterval ?? spawnInterval) * multiplier; if (float.IsNaN(num) || float.IsInfinity(num)) { string text = item.Prefab ?? item.SpawnSystem?.Name ?? "(unnamed)"; string text2 = (string.IsNullOrWhiteSpace(sourcePath) ? "inline YAML" : Path.GetFileName(sourcePath)); int? sourceLine = item.SourceLine; if (sourceLine.HasValue) { int valueOrDefault = sourceLine.GetValueOrDefault(); if (valueOrDefault > 0) { text2 = text2 + ":" + valueOrDefault.ToString(CultureInfo.InvariantCulture); } } throw new YamlException(header.Start, header.End, "'spawnIntervalMultiplier' produced a non-finite spawn interval for entry '" + text + "' at " + text2 + "."); } CanonicalSpawnSystemEntry canonicalSpawnSystemEntry = item; if (canonicalSpawnSystemEntry.SpawnSystem == null) { SpawnSystemSpawnDefinition spawnSystemSpawnDefinition = (canonicalSpawnSystemEntry.SpawnSystem = new SpawnSystemSpawnDefinition()); } item.SpawnSystem.SpawnInterval = Math.Max(0.01f, num); } } private static string SerializeYamlNode(YamlNode node) { YamlStream yamlStream = new YamlStream(new YamlDocument(node)); using StringWriter stringWriter = new StringWriter(CultureInfo.InvariantCulture); yamlStream.Save(stringWriter, assignAnchors: false); return stringWriter.ToString(); } private static string DescribeYamlNode(YamlNode node) { if (node is YamlScalarNode yamlScalarNode) { string text = yamlScalarNode.Value ?? ""; if (text.Length != 0) { return "scalar '" + text + "'"; } return "an empty scalar"; } if (node is YamlSequenceNode) { return "a nested sequence"; } if (node is YamlMappingNode) { return "a mapping"; } return "an unknown YAML node"; } private static string FormatYamlNodeLocation(string? sourcePath, Mark mark) { string text = (string.IsNullOrWhiteSpace(sourcePath) ? "inline YAML" : Path.GetFileName(sourcePath)); if (mark.Line > 0) { text = text + ":" + mark.Line.ToString(CultureInfo.InvariantCulture); } return text; } private static string FormatEntryParseFailure(Exception ex) { if (ex is YamlException ex2) { return ex2.Message; } return ex.Message; } private static void NormalizeConfiguration(List configuration) { if (configuration == null) { configuration = new List(); } Dictionary dictionary = new Dictionary(StringComparer.Ordinal); foreach (CanonicalSpawnSystemEntry item in configuration) { NormalizeEntry(item); string text = NormalizeOptionalRuleId(item.RuleId); if (text != null) { item.RuleId = text; continue; } string text2 = BuildRuleId(item); dictionary.TryGetValue(text2, out var value); item.RuleId = ((value == 0) ? text2 : $"{text2}#{value}"); dictionary[text2] = value + 1; } } internal static void NormalizeEntry(CanonicalSpawnSystemEntry entry) { entry.Prefab = NormalizeOptionalString(entry.Prefab); string context = entry.SpawnSystem?.Name ?? entry.Prefab ?? "(unnamed)"; NormalizeSpawnSystemSpawn(entry.SpawnSystem, context); } private static string BuildRuleId(CanonicalSpawnSystemEntry entry) { CanonicalSpawnSystemEntry entry2 = new CanonicalSpawnSystemEntry { Prefab = entry.Prefab, Enabled = true, SpawnSystem = ((entry.SpawnSystem != null) ? ConfigurationEntryCloneSupport.CloneSpawnSystemSpawnDefinition(entry.SpawnSystem) : null) }; return entry.Prefab + ":" + NetworkPayloadSyncSupport.ComputeSpawnSystemEntryIdentitySignature(entry2); } private static string? NormalizeOptionalRuleId(string? ruleId) { if (ruleId == null) { return null; } string text = ruleId.Trim(); if (text.Length != 0) { return text; } return null; } private static void ApplyIfReady(bool queueEspRefreshForLiveSystems, bool queueLiveSystemAttach = false) { int num = ComputeGameDataSignature(); if (num == 0) { return; } bool flag = ShouldApplyLocally(); string text = BuildApplyTargetSignature(num, flag); if ((!_forceApplyAfterSyncedCommit && string.Equals(_lastAppliedBuildTargetSignature, text, StringComparison.Ordinal)) || (string.Equals(BuildPipelineState.PendingBuildTargetSignature, text, StringComparison.Ordinal) && (BuildPipelineState.PreparedEntriesBuildInFlight || BuildPipelineState.CompletedPreparedEntriesBuildResult != null || BuildPipelineState.PendingCompiledTableBuild != null))) { return; } if (DropNSpawnPlugin.IsSourceOfTruth) { EnsurePrimaryOverrideConfigurationFileExists(); } bool flag2 = _lastAppliedGameDataSignature == num; if (!flag && flag2 && _lastAppliedDomainEnabled == false) { RecordAppliedState(num, flag, "", text); } else if (!flag) { ApplySelectedTableWithoutActiveBuild(num, flag, queueEspRefreshForLiveSystems, text); } else if (!RuntimeState.ConfigurationReady) { if (CanRetainCurrentCompiledTableWhilePending(num)) { RetainCurrentCompiledTableWhilePending(num, queueEspRefreshForLiveSystems, queueLiveSystemAttach, text); } else { ApplySelectedTableWithoutActiveBuild(num, flag, queueEspRefreshForLiveSystems, text); } } else if (ShouldDeferForExpandWorldDataBiomeReadyLocked()) { DeferExpandWorldDataBiomeReadyLocked(queueEspRefreshForLiveSystems, queueLiveSystemAttach, publishSyncedConfiguration: false); } else { QueuePreparedEntriesBuildLocked(num, flag, text, queueEspRefreshForLiveSystems); } } private static void RetainCurrentCompiledTableWhilePending(int gameDataSignature, bool queueEspRefreshForLiveSystems, bool queueLiveSystemAttach, string applyTargetSignature) { if (_activeCompiledTable == null) { return; } List liveSystems = GetLiveSystems(); if (queueLiveSystemAttach) { QueueLiveSystemAttachForTable(_activeCompiledTable, BuildPipelineState.PreparedEntriesBuildVersion, queueEspRefreshForLiveSystems, liveSystems); } else if (queueEspRefreshForLiveSystems) { foreach (SpawnSystem item in liveSystems) { QueueEspMarkerRefresh(item); } } RecordAppliedState(gameDataSignature, domainEnabled: true, _lastAppliedPreparedEntriesSignature, applyTargetSignature); } private static SpawnListSummary SummarizeSpawnLists(IEnumerable? spawnLists) { if (spawnLists == null) { return new SpawnListSummary { ListCount = 0, RowCount = 0, ContentHash = 0 }; } int num = 0; int num2 = 0; int num3 = 17; foreach (SpawnSystemList spawnList in spawnLists) { if ((Object)(object)spawnList == (Object)null) { continue; } num++; num3 = num3 * 31 + num; List spawners = spawnList.m_spawners; int num4 = spawners?.Count ?? 0; num2 += num4; num3 = num3 * 31 + num4; if (spawners == null) { continue; } foreach (SpawnData item in spawners) { string obj = (((Object)(object)item?.m_prefab != (Object)null) ? Utils.GetPrefabName(item.m_prefab) : (item?.m_name ?? "")); num3 = num3 * 31 + StringComparer.OrdinalIgnoreCase.GetHashCode(obj); num3 = num3 * 31 + ((item?.m_enabled ?? false) ? 1 : 0); } } return new SpawnListSummary { ListCount = num, RowCount = num2, ContentHash = num3 }; } private static void FreezeCompiledTableBaseline(CompiledSpawnSystemTable? table) { if (table != null) { SpawnListSummary spawnListSummary = SummarizeSpawnLists(table.Lists); table.BaselineListCount = spawnListSummary.ListCount; table.BaselineRowCount = spawnListSummary.RowCount; table.BaselineContentHash = spawnListSummary.ContentHash; } } private static int CountSpawnRows(CompiledSpawnSystemTable? table) { if (table == null) { return 0; } int num = 0; foreach (SpawnSystemList list in table.Lists) { if (list?.m_spawners != null) { num += list.m_spawners.Count; } } return num; } private static void RecordAppliedState(int gameDataSignature, bool domainEnabled, string preparedEntriesSignature, string applyTargetSignature) { _lastAppliedGameDataSignature = gameDataSignature; _lastAppliedDomainEnabled = domainEnabled; _lastAppliedConfigurationSignature = RuntimeState.ConfigurationSignature; _lastAppliedPreparedEntriesSignature = preparedEntriesSignature; _lastAppliedBuildTargetSignature = applyTargetSignature; _forceApplyAfterSyncedCommit = false; _lastCompletedGameDataSignature = gameDataSignature; if (BuildPipelineState.PendingGameDataSignature == gameDataSignature) { BuildPipelineState.PendingGameDataSignature = null; } } private static string BuildApplyTargetSignature(int gameDataSignature, bool domainEnabled) { return gameDataSignature.ToString(CultureInfo.InvariantCulture) + "|" + (domainEnabled ? "enabled" : "disabled") + "|" + (RuntimeState.ConfigurationReady ? "config_ready" : "config_pending") + "|" + RuntimeState.ConfigurationSignature + "|" + (PluginSettingsFacade.ShouldDisableLowTierBiomeGlobalKeySpawnSystemEntries() ? "low_tier_global_key_filter_on" : "low_tier_global_key_filter_off"); } private static void ApplySelectedTableWithoutActiveBuild(int gameDataSignature, bool domainEnabled, bool queueEspRefreshForLiveSystems, string applyTargetSignature) { CompiledSpawnSystemTable? selectedCompiledTableForCurrentState = GetSelectedCompiledTableForCurrentState(); List liveSystems = GetLiveSystems(); EnsureVanillaCompiledTableCurrentLocked(gameDataSignature); _activeCompiledTable = null; CompiledSpawnSystemTable selectedCompiledTableForCurrentState2 = GetSelectedCompiledTableForCurrentState(); QueueLiveSystemAttachForTable(selectedCompiledTableForCurrentState2, BuildPipelineState.PreparedEntriesBuildVersion, queueEspRefreshForLiveSystems, liveSystems); RetireCompiledTableAfterMigrationLocked(selectedCompiledTableForCurrentState, selectedCompiledTableForCurrentState2, liveSystems); RecordAppliedState(gameDataSignature, domainEnabled, "", applyTargetSignature); } private static void EnsureVanillaCompiledTableCurrentLocked(int gameDataSignature) { if (_vanillaCompiledTable == null || _vanillaCompiledTable.GameDataSignature != gameDataSignature) { CompiledSpawnSystemTable? vanillaCompiledTable = _vanillaCompiledTable; _vanillaCompiledTable = BuildVanillaCompiledTable(gameDataSignature); DestroyCompiledTableIfInactiveLocked(vanillaCompiledTable); } } private static void QueuePreparedEntriesBuildLocked(int gameDataSignature, bool domainEnabled, string applyTargetSignature, bool queueEspRefreshForLiveSystems) { ClearQueuedReconcileState(); BuildPipelineState.PreparedEntriesBuildVersion++; int preparedEntriesBuildVersion = BuildPipelineState.PreparedEntriesBuildVersion; BuildPipelineState.PreparedEntriesBuildInFlight = true; BuildPipelineState.CompletedPreparedEntriesBuildResult = null; BuildPipelineState.PendingCompiledTableBuild = null; BuildPipelineState.PendingBuildTargetSignature = applyTargetSignature; PendingPreparedEntriesBuildRequest pendingPreparedEntriesBuildRequest = new PendingPreparedEntriesBuildRequest { BuildVersion = preparedEntriesBuildVersion, GameDataSignature = gameDataSignature, DomainEnabled = domainEnabled, ApplyTargetSignature = applyTargetSignature, QueueEspRefreshForLiveSystems = queueEspRefreshForLiveSystems }; pendingPreparedEntriesBuildRequest.ConfigurationSnapshot.AddRange(RuntimeState.Configuration); BuildPipelineState.PendingPreparedEntriesBuildRequest = pendingPreparedEntriesBuildRequest; EnsurePreparedEntriesBuildWorkerLocked(); } private static void EnsurePreparedEntriesBuildWorkerLocked() { if (!BuildPipelineState.PreparedEntriesBuildWorkerRunning) { BuildPipelineState.PreparedEntriesBuildWorkerRunning = true; ThreadPool.QueueUserWorkItem(delegate { ProcessPreparedEntriesBuildWorker(); }); } } private static void ProcessPreparedEntriesBuildWorker() { while (true) { PendingPreparedEntriesBuildRequest pendingPreparedEntriesBuildRequest; lock (Sync) { pendingPreparedEntriesBuildRequest = BuildPipelineState.PendingPreparedEntriesBuildRequest; BuildPipelineState.PendingPreparedEntriesBuildRequest = null; if (pendingPreparedEntriesBuildRequest == null) { BuildPipelineState.PreparedEntriesBuildWorkerRunning = false; if (BuildPipelineState.CompletedPreparedEntriesBuildResult == null) { BuildPipelineState.PreparedEntriesBuildInFlight = false; } break; } } try { PreparedEntriesBuildResult preparedEntriesBuildResult = BuildPreparedEntriesResult(pendingPreparedEntriesBuildRequest); if (preparedEntriesBuildResult == null) { continue; } lock (Sync) { if (IsPreparedEntriesBuildCurrentLocked(preparedEntriesBuildResult.BuildVersion, preparedEntriesBuildResult.ApplyTargetSignature)) { PruneFinalizedPreparedEntryCacheLocked(preparedEntriesBuildResult.GameDataSignature, preparedEntriesBuildResult.Models.Select((PreparedSpawnSystemModel model) => model.EntrySignature)); BuildPipelineState.CompletedPreparedEntriesBuildResult = preparedEntriesBuildResult; BuildPipelineState.PreparedEntriesBuildInFlight = BuildPipelineState.PendingPreparedEntriesBuildRequest != null; } } } catch (Exception arg) { lock (Sync) { if (pendingPreparedEntriesBuildRequest != null && IsPreparedEntriesBuildCurrentLocked(pendingPreparedEntriesBuildRequest.BuildVersion, pendingPreparedEntriesBuildRequest.ApplyTargetSignature)) { BuildPipelineState.PreparedEntriesBuildInFlight = BuildPipelineState.PendingPreparedEntriesBuildRequest != null; BuildPipelineState.CompletedPreparedEntriesBuildResult = null; if (BuildPipelineState.PendingPreparedEntriesBuildRequest == null) { BuildPipelineState.PendingBuildTargetSignature = ""; BuildPipelineState.PendingGameDataSignature = null; } } } DropNSpawnPlugin.DropNSpawnLogger.LogError((object)$"Failed to prepare staged spawnsystem build. {arg}"); } } } private static bool IsPreparedEntriesBuildCurrentLocked(int buildVersion, string applyTargetSignature) { if (buildVersion == BuildPipelineState.PreparedEntriesBuildVersion) { return string.Equals(BuildPipelineState.PendingBuildTargetSignature, applyTargetSignature, StringComparison.Ordinal); } return false; } private static bool IsPreparedEntriesBuildCurrent(int buildVersion, string applyTargetSignature) { lock (Sync) { return IsPreparedEntriesBuildCurrentLocked(buildVersion, applyTargetSignature); } } private static PreparedEntriesBuildResult? BuildPreparedEntriesResult(PendingPreparedEntriesBuildRequest request) { PreparedEntriesBuildResult preparedEntriesBuildResult = new PreparedEntriesBuildResult { BuildVersion = request.BuildVersion, GameDataSignature = request.GameDataSignature, DomainEnabled = request.DomainEnabled, ApplyTargetSignature = request.ApplyTargetSignature, QueueEspRefreshForLiveSystems = request.QueueEspRefreshForLiveSystems }; for (int i = 0; i < request.ConfigurationSnapshot.Count; i++) { if (!IsPreparedEntriesBuildCurrent(request.BuildVersion, request.ApplyTargetSignature)) { return null; } CanonicalSpawnSystemEntry canonicalSpawnSystemEntry = request.ConfigurationSnapshot[i]; preparedEntriesBuildResult.Models.Add(new PreparedSpawnSystemModel { Entry = canonicalSpawnSystemEntry, EntrySignature = NetworkPayloadSyncSupport.ComputeSpawnSystemEntrySignature(canonicalSpawnSystemEntry), Context = CreateConfigurationContext(i, canonicalSpawnSystemEntry), RuntimeTimeOfDay = canonicalSpawnSystemEntry.SpawnSystem?.TimeOfDay }); } if (!IsPreparedEntriesBuildCurrent(request.BuildVersion, request.ApplyTargetSignature)) { return null; } return preparedEntriesBuildResult; } private static void PruneFinalizedPreparedEntryCacheLocked(int gameDataSignature, IEnumerable? activeEntrySignatures = null) { if (FinalizedPreparedEntryCache.Count == 0) { return; } HashSet hashSet = null; if (activeEntrySignatures != null) { hashSet = new HashSet(activeEntrySignatures.Where((string signature) => !string.IsNullOrWhiteSpace(signature)), StringComparer.Ordinal); } List list = new List(); foreach (var (item, finalizedPreparedEntryCacheEntry2) in FinalizedPreparedEntryCache) { if (finalizedPreparedEntryCacheEntry2 == null) { list.Add(item); } else if (finalizedPreparedEntryCacheEntry2.GameDataSignature != gameDataSignature) { list.Add(item); } else if (hashSet != null && !hashSet.Contains(finalizedPreparedEntryCacheEntry2.EntrySignature)) { list.Add(item); } } foreach (string item2 in list) { FinalizedPreparedEntryCache.Remove(item2); } } private static void RefreshResolvedBiomeMasksForSourceOfTruthConfigurationLocked() { if (DropNSpawnPlugin.IsSourceOfTruth) { RefreshResolvedBiomeMasksForConfiguration(RuntimeState.Configuration); } } private static void RefreshResolvedBiomeMasksForConfiguration(IEnumerable configuration) { foreach (CanonicalSpawnSystemEntry item in configuration ?? Enumerable.Empty()) { SpawnSystemSpawnDefinition spawnSystem = item.SpawnSystem; if (spawnSystem != null) { spawnSystem.ResolvedBiomeMask = BiomeResolutionSupport.ResolveBiomeMaskOrNull(spawnSystem.Biomes); } } } private static void PublishSyncedConfigurationOrDeferLocked() { if (DropNSpawnPlugin.IsSourceOfTruth) { if (ShouldDeferForExpandWorldDataBiomeReadyLocked()) { DeferExpandWorldDataBiomeReadyLocked(queueEspRefreshForLiveSystems: false, queueLiveSystemAttach: false, publishSyncedConfiguration: true); return; } DeferredBiomeState.ClearPublishSyncedConfiguration(); ConfigurationDomainHost.PublishSyncedPayload(DropNSpawnPlugin.IsSourceOfTruth, Descriptor, RuntimeState.Configuration, RuntimeState.ConfigurationSignature); } } private static bool ShouldDeferForExpandWorldDataBiomeReadyLocked() { return ShouldDeferForExpandWorldDataBiomeReady(RuntimeState.Configuration); } private static bool ShouldDeferForExpandWorldDataBiomeReady(IEnumerable configuration) { if (!BiomeResolutionSupport.IsExpandWorldDataPresent() || BiomeResolutionSupport.IsExpandWorldDataReadyOrUnavailable()) { return false; } foreach (CanonicalSpawnSystemEntry item in configuration ?? Enumerable.Empty()) { SpawnSystemSpawnDefinition spawnSystem = item.SpawnSystem; if (spawnSystem != null && BiomeResolutionSupport.ShouldWaitForExpandWorldDataBiomeResolution(spawnSystem.Biomes, spawnSystem.ResolvedBiomeMask)) { return true; } } return false; } private static void DeferExpandWorldDataBiomeReadyLocked(bool queueEspRefreshForLiveSystems, bool queueLiveSystemAttach, bool publishSyncedConfiguration) { DeferredBiomeState.Defer(queueEspRefreshForLiveSystems, queueLiveSystemAttach, publishSyncedConfiguration); } private static bool TryProcessDeferredExpandWorldDataBiomeReadyLocked() { if (!DeferredBiomeState.HasWork) { return false; } if (!BiomeResolutionSupport.IsExpandWorldDataReadyOrUnavailable()) { return false; } DeferredBiomeState.Consume(out var queueEspRefreshForLiveSystems, out var queueLiveSystemAttach, out var publishSyncedConfiguration); RefreshResolvedBiomeMasksForSourceOfTruthConfigurationLocked(); RuntimeState.ConfigurationSignature = NetworkPayloadSyncSupport.ComputeSpawnSystemConfigurationSignature(RuntimeState.Configuration); if (publishSyncedConfiguration) { ConfigurationDomainHost.PublishSyncedPayload(DropNSpawnPlugin.IsSourceOfTruth, Descriptor, RuntimeState.Configuration, RuntimeState.ConfigurationSignature); } ApplyIfReady(queueEspRefreshForLiveSystems, queueLiveSystemAttach); return true; } private static bool TryActivateCompletedPreparedEntriesBuildLocked() { if (BuildPipelineState.CompletedPreparedEntriesBuildResult == null) { return false; } PreparedEntriesBuildResult completedPreparedEntriesBuildResult = BuildPipelineState.CompletedPreparedEntriesBuildResult; BuildPipelineState.CompletedPreparedEntriesBuildResult = null; if (completedPreparedEntriesBuildResult.BuildVersion != BuildPipelineState.PreparedEntriesBuildVersion || !string.Equals(BuildPipelineState.PendingBuildTargetSignature, completedPreparedEntriesBuildResult.ApplyTargetSignature, StringComparison.Ordinal)) { return true; } PendingCompiledTableBuildState pendingCompiledTableBuildState = new PendingCompiledTableBuildState { BuildVersion = completedPreparedEntriesBuildResult.BuildVersion, GameDataSignature = completedPreparedEntriesBuildResult.GameDataSignature, DomainEnabled = completedPreparedEntriesBuildResult.DomainEnabled, EagerClientSyncBuild = !DropNSpawnPlugin.IsSourceOfTruth, ApplyTargetSignature = completedPreparedEntriesBuildResult.ApplyTargetSignature, QueueEspRefreshForLiveSystems = completedPreparedEntriesBuildResult.QueueEspRefreshForLiveSystems, PreviousActiveTable = _activeCompiledTable, PreviousVanillaTable = _vanillaCompiledTable }; pendingCompiledTableBuildState.Models.AddRange(completedPreparedEntriesBuildResult.Models); BuildPipelineState.PendingCompiledTableBuild = pendingCompiledTableBuildState; return true; } private static bool TryProcessPendingCompiledTableBuild(double deadline) { if (BuildPipelineState.PendingCompiledTableBuild == null) { return false; } PendingCompiledTableBuildState pendingCompiledTableBuild = BuildPipelineState.PendingCompiledTableBuild; if (pendingCompiledTableBuild.BuildVersion != BuildPipelineState.PreparedEntriesBuildVersion || !string.Equals(BuildPipelineState.PendingBuildTargetSignature, pendingCompiledTableBuild.ApplyTargetSignature, StringComparison.Ordinal)) { BuildPipelineState.PendingCompiledTableBuild = null; return true; } EnsureVanillaCompiledTableCurrentLocked(pendingCompiledTableBuild.GameDataSignature); if (pendingCompiledTableBuild.DomainEnabled && pendingCompiledTableBuild.NextFinalizeIndex < pendingCompiledTableBuild.Models.Count) { int num = 0; int num2 = ((!pendingCompiledTableBuild.EagerClientSyncBuild) ? 1 : int.MaxValue); while (pendingCompiledTableBuild.NextFinalizeIndex < pendingCompiledTableBuild.Models.Count && num < num2 && (pendingCompiledTableBuild.EagerClientSyncBuild || Time.realtimeSinceStartupAsDouble < deadline)) { if (TryFinalizePreparedSpawnSystemModelLocked(pendingCompiledTableBuild.Models[pendingCompiledTableBuild.NextFinalizeIndex++], pendingCompiledTableBuild.GameDataSignature, out PreparedSpawnSystemEntry finalizedEntry)) { pendingCompiledTableBuild.FinalizedEntries.Add(finalizedEntry); } num++; } return num > 0; } if (pendingCompiledTableBuild.DomainEnabled) { pendingCompiledTableBuild.PreparedEntriesSignature = ComputePreparedEntriesSignature(pendingCompiledTableBuild.FinalizedEntries); if (pendingCompiledTableBuild.BuildingActiveTable == null) { pendingCompiledTableBuild.BuildingActiveTable = new CompiledSpawnSystemTable { GameDataSignature = pendingCompiledTableBuild.GameDataSignature, Signature = pendingCompiledTableBuild.PreparedEntriesSignature }; pendingCompiledTableBuild.BuildingLiveEntries = new List(pendingCompiledTableBuild.FinalizedEntries.Count); return true; } if (pendingCompiledTableBuild.NextCompiledEntryIndex < pendingCompiledTableBuild.FinalizedEntries.Count) { int num3 = 0; int num4 = (pendingCompiledTableBuild.EagerClientSyncBuild ? int.MaxValue : 8); while (pendingCompiledTableBuild.NextCompiledEntryIndex < pendingCompiledTableBuild.FinalizedEntries.Count && num3 < num4 && (pendingCompiledTableBuild.EagerClientSyncBuild || Time.realtimeSinceStartupAsDouble < deadline)) { PreparedSpawnSystemEntry preparedSpawnSystemEntry = pendingCompiledTableBuild.FinalizedEntries[pendingCompiledTableBuild.NextCompiledEntryIndex++]; SpawnData val = preparedSpawnSystemEntry.Data.Clone(); StageCompiledRuntimeMetadata(pendingCompiledTableBuild.BuildingActiveTable, val, preparedSpawnSystemEntry.CustomDataPayload, preparedSpawnSystemEntry.RuntimeTimeOfDay); pendingCompiledTableBuild.BuildingLiveEntries.Add(val); num3++; } return num3 > 0; } if (pendingCompiledTableBuild.BuildingActiveTable.Lists.Count == 0) { pendingCompiledTableBuild.BuildingActiveTable.Lists.Add(CreateManagedSpawnList(pendingCompiledTableBuild.BuildingLiveEntries ?? new List())); FreezeCompiledTableBaseline(pendingCompiledTableBuild.BuildingActiveTable); return true; } } List liveSystems = GetLiveSystems(); CompiledSpawnSystemTable? selectedCompiledTableForCurrentState = GetSelectedCompiledTableForCurrentState(); _activeCompiledTable = (pendingCompiledTableBuild.DomainEnabled ? pendingCompiledTableBuild.BuildingActiveTable : null); QueueLiveSystemAttachForTable(_activeCompiledTable, pendingCompiledTableBuild.BuildVersion, pendingCompiledTableBuild.QueueEspRefreshForLiveSystems, liveSystems); RetireCompiledTableAfterMigrationLocked(selectedCompiledTableForCurrentState, _activeCompiledTable, liveSystems); DestroyCompiledTableIfInactiveLocked(pendingCompiledTableBuild.PreviousActiveTable); DestroyCompiledTableIfInactiveLocked(pendingCompiledTableBuild.PreviousVanillaTable); RecordAppliedState(pendingCompiledTableBuild.GameDataSignature, pendingCompiledTableBuild.DomainEnabled, pendingCompiledTableBuild.PreparedEntriesSignature, pendingCompiledTableBuild.ApplyTargetSignature); BuildPipelineState.PendingBuildTargetSignature = ""; BuildPipelineState.PendingCompiledTableBuild = null; return true; } private static bool TryFinalizePreparedSpawnSystemModelLocked(PreparedSpawnSystemModel model, int gameDataSignature, out PreparedSpawnSystemEntry? finalizedEntry) { //IL_00b9: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Expected O, but got Unknown finalizedEntry = null; if (model == null || model.Entry == null) { return false; } string text = (model.EntrySignature = ((model.EntrySignature.Length > 0) ? model.EntrySignature : NetworkPayloadSyncSupport.ComputeSpawnSystemEntrySignature(model.Entry))); string key = gameDataSignature.ToString(CultureInfo.InvariantCulture) + "|" + text; if (FinalizedPreparedEntryCache.TryGetValue(key, out FinalizedPreparedEntryCacheEntry value) && value != null && value.GameDataSignature == gameDataSignature) { if (ShouldSkipLowTierBiomeGlobalKeySpawnSystemEntry(value.Data)) { return false; } finalizedEntry = new PreparedSpawnSystemEntry { Entry = model.Entry, Data = value.Data.Clone(), CustomDataPayload = value.CustomDataPayload, RuntimeTimeOfDay = value.RuntimeTimeOfDay }; return true; } SpawnData val = new SpawnData(); if (!ApplyEntry(val, model.Entry, model.Context, applyCustomData: false)) { return false; } if (ShouldSkipLowTierBiomeGlobalKeySpawnSystemEntry(val)) { return false; } SpawnSystemCustomDataSupport.PreparedPayload customDataPayload = SpawnSystemCustomDataSupport.BuildPreparedPayload(val, model.Entry, model.Context); FinalizedPreparedEntryCache[key] = new FinalizedPreparedEntryCacheEntry { GameDataSignature = gameDataSignature, EntrySignature = text, Data = val.Clone(), CustomDataPayload = customDataPayload, RuntimeTimeOfDay = model.RuntimeTimeOfDay }; finalizedEntry = new PreparedSpawnSystemEntry { Entry = model.Entry, Data = val, CustomDataPayload = customDataPayload, RuntimeTimeOfDay = model.RuntimeTimeOfDay }; return true; } private static void StageCompiledRuntimeMetadata(CompiledSpawnSystemTable? table, SpawnData spawnData, SpawnSystemCustomDataSupport.PreparedPayload? customDataPayload, TimeOfDayDefinition? timeOfDay) { if (table != null && spawnData != null) { if (timeOfDay != null && timeOfDay.HasValues()) { table.RuntimeTimeOfDayBySpawnData[spawnData] = timeOfDay; } if (customDataPayload != null) { table.CustomPayloadsBySpawnData[spawnData] = customDataPayload; } } } private static void RemoveCompiledRuntimeState(CompiledSpawnSystemTable? table) { if (table == null) { return; } InvalidateRuntimeTimeOfDayPhaseMarker(); foreach (SpawnData key in table.RuntimeTimeOfDayBySpawnData.Keys) { TimeOfDayBySpawnData.Remove(key); } foreach (SpawnData key2 in table.CustomPayloadsBySpawnData.Keys) { SpawnSystemCustomDataSupport.ApplyPreparedPayload(key2, null); } _hasRuntimeTimeOfDayOverrides = TimeOfDayBySpawnData.Count > 0; } private static void RetireCompiledTableAfterMigrationLocked(CompiledSpawnSystemTable? previousSelectedTable, CompiledSpawnSystemTable? nextSelectedTable, IEnumerable liveSystems) { if (previousSelectedTable == null || previousSelectedTable == nextSelectedTable) { return; } PendingCompiledTableRetirement pendingCompiledTableRetirement = PendingCompiledTableRetirements.FirstOrDefault((PendingCompiledTableRetirement retirement) => retirement.Table == previousSelectedTable); if (pendingCompiledTableRetirement == null) { pendingCompiledTableRetirement = new PendingCompiledTableRetirement { Table = previousSelectedTable }; PendingCompiledTableRetirements.Add(pendingCompiledTableRetirement); } foreach (SpawnSystem liveSystem in liveSystems) { if ((Object)(object)liveSystem != (Object)null) { pendingCompiledTableRetirement.RemainingSystemIds.Add(((Object)liveSystem).GetInstanceID()); } } if (pendingCompiledTableRetirement.RemainingSystemIds.Count == 0) { FinalizeCompiledTableRetirementLocked(pendingCompiledTableRetirement); } } private static void MarkSystemMigratedFromRetiredTablesLocked(int systemId) { if (systemId == 0 || PendingCompiledTableRetirements.Count == 0) { return; } List list = null; foreach (PendingCompiledTableRetirement pendingCompiledTableRetirement in PendingCompiledTableRetirements) { if (pendingCompiledTableRetirement.RemainingSystemIds.Remove(systemId) && pendingCompiledTableRetirement.RemainingSystemIds.Count <= 0) { if (list == null) { list = new List(); } list.Add(pendingCompiledTableRetirement); } } if (list == null) { return; } foreach (PendingCompiledTableRetirement item in list) { FinalizeCompiledTableRetirementLocked(item); } } private static void FinalizeAllPendingCompiledTableRetirementsLocked() { if (PendingCompiledTableRetirements.Count == 0) { return; } foreach (PendingCompiledTableRetirement item in PendingCompiledTableRetirements.ToList()) { FinalizeCompiledTableRetirementLocked(item); } } private static void FinalizeCompiledTableRetirementLocked(PendingCompiledTableRetirement retirement) { if (retirement != null) { PendingCompiledTableRetirements.Remove(retirement); RemoveCompiledRuntimeState(retirement.Table); DestroyReplacedCompiledTable(retirement.Table); } } private static void DestroyCompiledTableIfInactiveLocked(CompiledSpawnSystemTable? table) { if (table != null && !PendingCompiledTableRetirements.Any((PendingCompiledTableRetirement retirement) => retirement.Table == table)) { DestroyReplacedCompiledTable(table); } } internal static bool ApplyEntry(SpawnData data, CanonicalSpawnSystemEntry entry, string context, bool applyCustomData = true) { //IL_00f9: Unknown result type (might be due to invalid IL or missing references) //IL_00fe: Unknown result type (might be due to invalid IL or missing references) //IL_0128: Unknown result type (might be due to invalid IL or missing references) //IL_012a: Unknown result type (might be due to invalid IL or missing references) //IL_0152: Unknown result type (might be due to invalid IL or missing references) //IL_0154: Unknown result type (might be due to invalid IL or missing references) bool flag = true; bool flag2 = false; string text = null; SpawnSystemSpawnDefinition spawnSystem = entry.SpawnSystem; if (spawnSystem != null && spawnSystem.Name != null) { data.m_name = spawnSystem.Name; } data.m_enabled = entry.Enabled; if (string.IsNullOrWhiteSpace(entry.Prefab)) { WarnInvalidEntry("Entry '" + context + "' is missing required prefab."); flag = false; } else { string prefab = entry.Prefab; GameObject val = ResolvePrefab(prefab); if ((Object)(object)val == (Object)null) { WarnInvalidEntry("Entry '" + context + "' references unknown prefab '" + prefab + "'."); flag = false; } else { data.m_prefab = val; flag2 = true; text = ((Object)val).name; } } if (spawnSystem?.Name == null && flag2 && !string.IsNullOrWhiteSpace(text)) { data.m_name = text; } if (spawnSystem != null && spawnSystem.ResolvedBiomeMask.HasValue) { data.m_biome = spawnSystem.ResolvedBiomeMask.Value; } else if (spawnSystem?.Biomes != null) { if (!TryParseBiomes(spawnSystem.Biomes, context, out var biomes)) { flag = false; } else { data.m_biome = biomes; } } if (spawnSystem?.BiomeAreas != null) { if (!TryParseBiomeAreas(spawnSystem.BiomeAreas, context, out var biomeAreas)) { flag = false; } else { data.m_biomeArea = biomeAreas; } } if (spawnSystem != null && spawnSystem.RequiredGlobalKey != null) { data.m_requiredGlobalKey = spawnSystem.RequiredGlobalKey; } if (spawnSystem?.RequiredEnvironments != null) { data.m_requiredEnvironments = (from value in spawnSystem.RequiredEnvironments select (value ?? "").Trim() into value where value.Length > 0 select value).ToList(); } TimeOfDayDefinition timeOfDayDefinition = spawnSystem?.TimeOfDay; if (timeOfDayDefinition != null) { TimeOfDayFormatting.GetBroadSpawnFlags(timeOfDayDefinition, out var allowDay, out var allowNight); data.m_spawnAtDay = allowDay; data.m_spawnAtNight = allowNight; } if (spawnSystem != null && spawnSystem.MinLevel.HasValue) { data.m_minLevel = Math.Max(1, spawnSystem.MinLevel.Value); } if (spawnSystem != null && spawnSystem.MaxLevel.HasValue) { data.m_maxLevel = Math.Max(1, spawnSystem.MaxLevel.Value); } else if (spawnSystem != null && spawnSystem.MinLevel.HasValue) { data.m_maxLevel = Math.Max(data.m_minLevel, data.m_maxLevel); } if (spawnSystem != null && spawnSystem.LevelUpMinCenterDistance.HasValue) { data.m_levelUpMinCenterDistance = spawnSystem.LevelUpMinCenterDistance.Value; } if (spawnSystem != null && spawnSystem.OverrideLevelUpChance.HasValue) { data.m_overrideLevelupChance = spawnSystem.OverrideLevelUpChance.Value; } if (spawnSystem != null && spawnSystem.MaxSpawned.HasValue) { data.m_maxSpawned = Math.Max(0, spawnSystem.MaxSpawned.Value); } if (spawnSystem != null && spawnSystem.SpawnInterval.HasValue) { data.m_spawnInterval = Math.Max(0.01f, spawnSystem.SpawnInterval.Value); } if (spawnSystem != null && spawnSystem.SpawnChance.HasValue) { data.m_spawnChance = spawnSystem.SpawnChance.Value; } if (spawnSystem != null && spawnSystem.NoSpawnRadius.HasValue) { data.m_spawnDistance = Math.Max(0f, spawnSystem.NoSpawnRadius.Value); } if (spawnSystem != null && spawnSystem.SpawnRadiusMin.HasValue) { data.m_spawnRadiusMin = Math.Max(0f, spawnSystem.SpawnRadiusMin.Value); } if (spawnSystem != null && spawnSystem.SpawnRadiusMax.HasValue) { data.m_spawnRadiusMax = Math.Max(0f, spawnSystem.SpawnRadiusMax.Value); } if (spawnSystem != null && spawnSystem.GroupSizeMin.HasValue) { data.m_groupSizeMin = Math.Max(1, spawnSystem.GroupSizeMin.Value); } if (spawnSystem != null && spawnSystem.GroupSizeMax.HasValue) { data.m_groupSizeMax = Math.Max(1, spawnSystem.GroupSizeMax.Value); } else if (spawnSystem != null && spawnSystem.GroupSizeMin.HasValue) { data.m_groupSizeMax = Math.Max(data.m_groupSizeMin, data.m_groupSizeMax); } if (spawnSystem != null && spawnSystem.GroupRadius.HasValue) { data.m_groupRadius = Math.Max(0f, spawnSystem.GroupRadius.Value); } if (spawnSystem != null && spawnSystem.MinAltitude.HasValue) { data.m_minAltitude = spawnSystem.MinAltitude.Value; } if (spawnSystem != null && spawnSystem.MaxAltitude.HasValue) { data.m_maxAltitude = spawnSystem.MaxAltitude.Value; } if (spawnSystem != null && spawnSystem.MinTilt.HasValue) { data.m_minTilt = spawnSystem.MinTilt.Value; } if (spawnSystem != null && spawnSystem.MaxTilt.HasValue) { data.m_maxTilt = spawnSystem.MaxTilt.Value; } ApplyExclusiveZoneToggle(spawnSystem?.InForest, ref data.m_inForest, ref data.m_outsideForest); ApplyExclusiveZoneToggle(spawnSystem?.InLava, ref data.m_inLava, ref data.m_outsideLava); if (spawnSystem != null && spawnSystem.CanSpawnCloseToPlayer.HasValue) { data.m_canSpawnCloseToPlayer = spawnSystem.CanSpawnCloseToPlayer.Value; } if (spawnSystem != null && spawnSystem.InsidePlayerBase.HasValue) { data.m_insidePlayerBase = spawnSystem.InsidePlayerBase.Value; } if (spawnSystem != null && spawnSystem.MinOceanDepth.HasValue) { data.m_minOceanDepth = spawnSystem.MinOceanDepth.Value; } if (spawnSystem != null && spawnSystem.MaxOceanDepth.HasValue) { data.m_maxOceanDepth = spawnSystem.MaxOceanDepth.Value; } if (spawnSystem != null && spawnSystem.HuntPlayer.HasValue) { data.m_huntPlayer = spawnSystem.HuntPlayer.Value; } if (spawnSystem != null && spawnSystem.GroundOffset.HasValue) { data.m_groundOffset = spawnSystem.GroundOffset.Value; } if (spawnSystem != null && spawnSystem.GroundOffsetRandom.HasValue) { data.m_groundOffsetRandom = spawnSystem.GroundOffsetRandom.Value; } if (spawnSystem != null && spawnSystem.MinDistanceFromCenter.HasValue) { data.m_minDistanceFromCenter = spawnSystem.MinDistanceFromCenter.Value; } if (spawnSystem != null && spawnSystem.MaxDistanceFromCenter.HasValue) { data.m_maxDistanceFromCenter = spawnSystem.MaxDistanceFromCenter.Value; } if (flag && applyCustomData) { SpawnSystemCustomDataSupport.ApplyCustomData(data, entry, context); } return flag; } private static void ApplyRuntimeMetadata(SpawnData spawnData, TimeOfDayDefinition? timeOfDay) { if (spawnData != null) { InvalidateRuntimeTimeOfDayPhaseMarker(); if (timeOfDay != null && timeOfDay.HasValues()) { TimeOfDayBySpawnData[spawnData] = timeOfDay; } else { TimeOfDayBySpawnData.Remove(spawnData); } _hasRuntimeTimeOfDayOverrides = TimeOfDayBySpawnData.Count > 0; } } private static void ClearRuntimeMetadata(SpawnSystem system) { if ((Object)(object)system == (Object)null) { return; } InvalidateRuntimeTimeOfDayPhaseMarker(); foreach (SpawnSystemList spawnList in system.m_spawnLists) { foreach (SpawnData spawner in spawnList.m_spawners) { TimeOfDayBySpawnData.Remove(spawner); } } _hasRuntimeTimeOfDayOverrides = TimeOfDayBySpawnData.Count > 0; } private static int ComputeGameDataSignature() { int frameCount = Time.frameCount; if (_cachedGameDataSignatureFrame == frameCount) { return _cachedGameDataSignatureValue; } int num = ComputeGameDataSignatureCore(); _cachedGameDataSignatureFrame = frameCount; _cachedGameDataSignatureValue = num; return num; } private static int ComputeGameDataSignatureCore() { if ((Object)(object)ZNetScene.instance == (Object)null || (Object)(object)ObjectDB.instance == (Object)null) { return 0; } List list = new List(); SpawnSystem zoneCtrlPrefabSpawnSystem = GetZoneCtrlPrefabSpawnSystem(); if ((Object)(object)zoneCtrlPrefabSpawnSystem != (Object)null) { list.Add(zoneCtrlPrefabSpawnSystem); } else { list = GetLiveSystems(); } if (list.Count == 0) { return 0; } int num = 17; num = num * 31 + ((Object)ZNetScene.instance).GetInstanceID(); num = num * 31 + ((Object)ObjectDB.instance).GetInstanceID(); num = HashConfiguredSpawnSystemResolutionKeys(num); foreach (SpawnSystem item in list) { num = num * 31 + ((Object)item).GetInstanceID(); num = num * 31 + item.m_spawnLists.Count; foreach (SpawnSystemList spawnList in item.m_spawnLists) { num = num * 31 + spawnList.m_spawners.Count; foreach (SpawnData spawner in spawnList.m_spawners) { num = num * 31 + (((Object)(object)spawner?.m_prefab != (Object)null) ? ((Object)spawner.m_prefab).GetInstanceID() : 0); } } } return num; } private static int HashConfiguredSpawnSystemResolutionKeys(int hash) { EnsureConfiguredSpawnSystemResolutionKeysCached(); foreach (string cachedConfiguredSpawnSystemResolutionKey in _cachedConfiguredSpawnSystemResolutionKeys) { hash = hash * 31 + StringComparer.OrdinalIgnoreCase.GetHashCode(cachedConfiguredSpawnSystemResolutionKey); } return hash; } private static void EnsureConfiguredSpawnSystemResolutionKeysCached() { if ((Object)(object)ZNetScene.instance == (Object)null) { _cachedConfiguredSpawnSystemResolutionKeys.Clear(); _cachedConfiguredSpawnSystemResolutionKeysSignature = ""; _cachedConfiguredSpawnSystemResolutionKeysSceneStamp = int.MinValue; return; } int num = ComputeConfiguredSpawnSystemResolutionKeysSceneStamp(); if (_cachedConfiguredSpawnSystemResolutionKeysSceneStamp != num || !string.Equals(_cachedConfiguredSpawnSystemResolutionKeysSignature, RuntimeState.ConfigurationSignature, StringComparison.Ordinal)) { _cachedConfiguredSpawnSystemResolutionKeys.Clear(); _cachedConfiguredSpawnSystemResolutionKeys.AddRange(BuildConfiguredSpawnSystemResolutionKeys()); _cachedConfiguredSpawnSystemResolutionKeys.Sort(StringComparer.OrdinalIgnoreCase); _cachedConfiguredSpawnSystemResolutionKeysSignature = RuntimeState.ConfigurationSignature; _cachedConfiguredSpawnSystemResolutionKeysSceneStamp = num; } } private static int ComputeConfiguredSpawnSystemResolutionKeysSceneStamp() { if ((Object)(object)ZNetScene.instance == (Object)null) { return int.MinValue; } return ((17 * 31 + ((Object)ZNetScene.instance).GetInstanceID()) * 31 + ZNetScene.instance.m_prefabs.Count) * 31 + ZNetScene.instance.m_nonNetViewPrefabs.Count; } private static IEnumerable BuildConfiguredSpawnSystemResolutionKeys() { HashSet seen = new HashSet(StringComparer.OrdinalIgnoreCase); foreach (CanonicalSpawnSystemEntry item in RuntimeState.Configuration) { string text = ReferenceRefreshSupport.NormalizeKey(item.Prefab); if (text.Length != 0) { GameObject? obj = ResolvePrefab(text); string text2 = text + ":" + ((obj != null) ? ((Object)obj).GetInstanceID() : 0).ToString(CultureInfo.InvariantCulture); if (seen.Add(text2)) { yield return text2; } } } } private static bool IsOverrideConfigurationFileName(string fileName) { return DomainConfigurationFileSupport.IsOverrideConfigurationFileName("spawnsystem", fileName); } private static bool TryEnumerateReferenceSpawnData(IEnumerable? lists, out IEnumerable? spawnData) { List list = (from current in lists?.Where((SpawnSystemList spawnList) => (Object)(object)spawnList != (Object)null).SelectMany((SpawnSystemList spawnList) => spawnList.m_spawners ?? new List()) where current != null select current).ToList() ?? new List(); if (list.Count == 0) { spawnData = null; return false; } spawnData = list; return true; } private static bool TryParseBiomes(List names, string context, out Biome biomes) { //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Expected I4, but got Unknown biomes = (Biome)0; foreach (string name in names) { string text = (name ?? "").Trim(); if (text.Length != 0) { if (!TryResolveBiomeToken(text, out var biome)) { WarnInvalidEntry("Entry '" + context + "' contains unknown biome '" + text + "'."); biomes = (Biome)0; return false; } biomes |= biome; } } return true; } private static bool TryResolveBiomeToken(string name, out Biome biome) { if (BiomeResolutionSupport.TryResolveBiomeToken(name, out biome)) { return true; } biome = (Biome)0; return false; } private static bool TryParseBiomeAreas(List names, string context, out BiomeArea biomeAreas) { //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Invalid comparison between Unknown and I4 //IL_007a: 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_007d: Expected I4, but got Unknown biomeAreas = (BiomeArea)0; foreach (string name in names) { string text = (name ?? "").Trim(); if (text.Length != 0) { if (!Enum.TryParse(text, ignoreCase: true, out BiomeArea result)) { WarnInvalidEntry("Entry '" + context + "' contains unknown biomeArea '" + text + "'."); biomeAreas = (BiomeArea)0; return false; } if ((int)result == 3) { biomeAreas = (BiomeArea)3; return true; } biomeAreas |= result; } } return true; } private static List ConvertBiomes(Biome biomes) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) return BiomeResolutionSupport.ConvertBiomeMaskToNames(biomes); } private static List ConvertBiomeAreas(BiomeArea biomeAreas) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Invalid comparison between Unknown and I4 //IL_000b: 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_0047: 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_004a: Invalid comparison between Unknown and I4 //IL_004c: 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_004e: 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) if ((int)biomeAreas == 3) { return new List { ((object)(BiomeArea)3/*cast due to .constrained prefix*/).ToString() }; } List list = new List(); foreach (BiomeArea value in Enum.GetValues(typeof(BiomeArea))) { if ((int)value != 3 && (BiomeArea)(biomeAreas & value) == value) { list.Add(((object)value/*cast due to .constrained prefix*/).ToString()); } } return list; } private static GameObject? ResolvePrefab(string prefabName) { if ((Object)(object)ZNetScene.instance == (Object)null || string.IsNullOrWhiteSpace(prefabName)) { return null; } GameObject prefab = ZNetScene.instance.GetPrefab(prefabName); if ((Object)(object)prefab != (Object)null) { return prefab; } return ZNetScene.instance.m_prefabs.Concat(ZNetScene.instance.m_nonNetViewPrefabs).FirstOrDefault((Func)((GameObject candidate) => string.Equals(((Object)candidate).name, prefabName, StringComparison.OrdinalIgnoreCase))); } private static string CreateConfigurationContext(int index, CanonicalSpawnSystemEntry entry) { string text = "entry[" + index.ToString(CultureInfo.InvariantCulture) + "]"; string text2 = entry.Prefab ?? "(no prefab)"; string text3 = entry.SpawnSystem?.Name ?? "(no name)"; string text4 = text + " " + text2 + " / " + text3; if (string.IsNullOrWhiteSpace(entry.SourcePath)) { return text4; } string text5 = Path.GetFileName(entry.SourcePath); if (entry.SourceLine.HasValue) { text5 = text5 + ":" + entry.SourceLine.Value.ToString(CultureInfo.InvariantCulture); } return text4 + " @ " + text5; } private static string FormatYamlExceptionLocation(Exception ex) { if (!(ex is YamlException { Start: var start })) { return ""; } if (start.Line <= 0) { return ""; } return " at line " + start.Line.ToString(CultureInfo.InvariantCulture); } private static string? NormalizeOptionalString(string? value) { return value?.Trim(); } private static void NormalizeSpawnSystemConditionFields(SpawnSystemSpawnDefinition? conditions, string context) { if (conditions != null) { FloatRangeDefinition? tilt = conditions.Tilt; if (tilt != null && tilt.HasValues()) { conditions.MinTilt = RangeFormatting.GetMin(conditions.Tilt, conditions.MinTilt); conditions.MaxTilt = RangeFormatting.GetMax(conditions.Tilt, conditions.MinTilt, conditions.MaxTilt); float? min = conditions.MinTilt; float? max = conditions.MaxTilt; NormalizeSpawnSystemFloatRange(ref min, ref max, context, "tilt"); conditions.MinTilt = min; conditions.MaxTilt = max; } FloatRangeDefinition? altitude = conditions.Altitude; if (altitude != null && altitude.HasValues()) { conditions.MinAltitude = RangeFormatting.GetMin(conditions.Altitude, conditions.MinAltitude); conditions.MaxAltitude = RangeFormatting.GetMax(conditions.Altitude, conditions.MinAltitude, conditions.MaxAltitude); float? min2 = conditions.MinAltitude; float? max2 = conditions.MaxAltitude; NormalizeSpawnSystemFloatRange(ref min2, ref max2, context, "altitude"); conditions.MinAltitude = min2; conditions.MaxAltitude = max2; } FloatRangeDefinition? oceanDepth = conditions.OceanDepth; if (oceanDepth != null && oceanDepth.HasValues()) { conditions.MinOceanDepth = RangeFormatting.GetMin(conditions.OceanDepth, conditions.MinOceanDepth); conditions.MaxOceanDepth = RangeFormatting.GetMax(conditions.OceanDepth, conditions.MinOceanDepth, conditions.MaxOceanDepth); float? min3 = conditions.MinOceanDepth; float? max3 = conditions.MaxOceanDepth; NormalizeSpawnSystemFloatRange(ref min3, ref max3, context, "oceanDepth"); conditions.MinOceanDepth = min3; conditions.MaxOceanDepth = max3; } FloatRangeDefinition? distanceFromCenter = conditions.DistanceFromCenter; if (distanceFromCenter != null && distanceFromCenter.HasValues()) { conditions.MinDistanceFromCenter = RangeFormatting.GetMin(conditions.DistanceFromCenter, conditions.MinDistanceFromCenter); conditions.MaxDistanceFromCenter = RangeFormatting.GetMax(conditions.DistanceFromCenter, conditions.MinDistanceFromCenter, conditions.MaxDistanceFromCenter); float? min4 = conditions.MinDistanceFromCenter; float? max4 = conditions.MaxDistanceFromCenter; NormalizeSpawnSystemFloatRange(ref min4, ref max4, context, "distanceFromCenter"); conditions.MinDistanceFromCenter = min4; conditions.MaxDistanceFromCenter = max4; } conditions.Biomes = NormalizeOptionalStringList(conditions.Biomes); conditions.BiomeAreas = NormalizeOptionalStringList(conditions.BiomeAreas); conditions.RequiredGlobalKey = NormalizeOptionalString(conditions.RequiredGlobalKey); conditions.RequiredEnvironments = NormalizeOptionalStringList(conditions.RequiredEnvironments); conditions.TimeOfDay?.Normalize(); } } private static void NormalizeSpawnSystemSpawn(SpawnSystemSpawnDefinition? spawn, string context) { if (spawn != null) { spawn.Name = NormalizeOptionalString(spawn.Name); IntRangeDefinition? level = spawn.Level; if (level != null && level.HasValues()) { spawn.MinLevel = RangeFormatting.GetMin(spawn.Level, spawn.MinLevel); spawn.MaxLevel = RangeFormatting.GetMax(spawn.Level, spawn.MinLevel, spawn.MaxLevel); int? min = spawn.MinLevel; int? max = spawn.MaxLevel; NormalizeSpawnSystemIntRange(ref min, ref max, context, "level"); spawn.MinLevel = min; spawn.MaxLevel = max; } FloatRangeDefinition? spawnRadius = spawn.SpawnRadius; if (spawnRadius != null && spawnRadius.HasValues()) { spawn.SpawnRadiusMin = RangeFormatting.GetMin(spawn.SpawnRadius, spawn.SpawnRadiusMin); spawn.SpawnRadiusMax = RangeFormatting.GetMax(spawn.SpawnRadius, spawn.SpawnRadiusMin, spawn.SpawnRadiusMax); float? min2 = spawn.SpawnRadiusMin; float? max2 = spawn.SpawnRadiusMax; NormalizeSpawnSystemFloatRange(ref min2, ref max2, context, "spawnRadius"); spawn.SpawnRadiusMin = min2; spawn.SpawnRadiusMax = max2; } IntRangeDefinition? groupSize = spawn.GroupSize; if (groupSize != null && groupSize.HasValues()) { spawn.GroupSizeMin = RangeFormatting.GetMin(spawn.GroupSize, spawn.GroupSizeMin); spawn.GroupSizeMax = RangeFormatting.GetMax(spawn.GroupSize, spawn.GroupSizeMin, spawn.GroupSizeMax); int? min3 = spawn.GroupSizeMin; int? max3 = spawn.GroupSizeMax; NormalizeSpawnSystemIntRange(ref min3, ref max3, context, "groupSize"); spawn.GroupSizeMin = min3; spawn.GroupSizeMax = max3; } NormalizeSpawnSystemConditionFields(spawn, context); NormalizeSpawnSystemCustomDataFields(spawn); } } private static void NormalizeSpawnSystemCustomDataFields(SpawnSystemSpawnDefinition? modifiers) { if (modifiers != null) { modifiers.Objects = NormalizeOptionalStringList(modifiers.Objects); modifiers.Fields = NormalizeOptionalStringDictionary(modifiers.Fields); modifiers.Data = NormalizeOptionalString(modifiers.Data); modifiers.Faction = NormalizeOptionalString(modifiers.Faction); } } private static List? NormalizeOptionalStringList(List? values) { if (values == null) { return null; } List list = (from value in values select (value ?? "").Trim() into value where value.Length > 0 select value).ToList(); if (list.Count != 0) { return list; } return null; } private static Dictionary? NormalizeOptionalStringDictionary(Dictionary? values) { if (values == null) { return null; } Dictionary dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); foreach (KeyValuePair value in values) { object obj; string text3; (obj, text3) = (KeyValuePair)(ref value); if (obj == null) { obj = ""; } string text4 = ((string)obj).Trim(); if (text4.Length != 0) { dictionary[text4] = (text3 ?? "").Trim(); } } if (dictionary.Count != 0) { return dictionary; } return null; } private static void NormalizeSpawnSystemIntRange(ref int? min, ref int? max, string context, string fieldName) { int? min2 = min; int? max2 = max; if (RangeFormatting.NormalizeAscending(ref min, ref max)) { WarnInvalidEntry("Entry '" + context + "' contains reversed " + fieldName + " range '" + RangeFormatting.FormatShorthand(RangeFormatting.From(min2, max2)) + "'. Normalized to '" + RangeFormatting.FormatShorthand(RangeFormatting.From(min, max)) + "'."); } } private static void NormalizeSpawnSystemFloatRange(ref float? min, ref float? max, string context, string fieldName) { float? min2 = min; float? max2 = max; if (RangeFormatting.NormalizeAscending(ref min, ref max)) { WarnInvalidEntry("Entry '" + context + "' contains reversed " + fieldName + " range '" + RangeFormatting.FormatShorthand(RangeFormatting.From(min2, max2)) + "'. Normalized to '" + RangeFormatting.FormatShorthand(RangeFormatting.From(min, max)) + "'."); } } private static string? NormalizeNullable(string? value) { string text = (value ?? "").Trim(); if (text.Length != 0) { return text; } return null; } private static string? NormalizeReferencePrefabName(GameObject? prefab) { if (!((Object)(object)prefab == (Object)null)) { return ((Object)prefab).name; } return null; } internal static void EnterRequiredGlobalKeyEvaluation() { _requiredGlobalKeyEvaluationDepth++; } internal static void ExitRequiredGlobalKeyEvaluation() { if (_requiredGlobalKeyEvaluationDepth > 0) { _requiredGlobalKeyEvaluationDepth--; } } internal static bool TryEvaluateExtendedRequiredGlobalKey(ZoneSystem? zoneSystem, string? rawValue, out bool result) { result = false; if (_requiredGlobalKeyEvaluationDepth <= 0 || (Object)(object)zoneSystem == (Object)null || !TryParseRequiredGlobalKeyThreshold(rawValue, out string key, out int amount)) { return false; } string s = default(string); if (!zoneSystem.GetGlobalKey(key, ref s) || !int.TryParse(s, NumberStyles.Any, CultureInfo.InvariantCulture, out var result2)) { result = false; return true; } result = result2 >= amount; return true; } internal static bool TryRewriteExtendedGlobalKeyMutation(ZoneSystem? zoneSystem, string? rawValue, out string rewrittenValue) { rewrittenValue = rawValue ?? ""; if ((Object)(object)zoneSystem == (Object)null || !TryParseGlobalKeyMutation(rawValue, out string key, out int delta)) { return false; } string s = default(string); if (zoneSystem.GetGlobalKey(key, ref s) && int.TryParse(s, NumberStyles.Any, CultureInfo.InvariantCulture, out var result)) { rewrittenValue = key + " " + (result + delta).ToString(CultureInfo.InvariantCulture); } else { rewrittenValue = key + " " + delta.ToString(CultureInfo.InvariantCulture); } return true; } internal static void ConsumeExtendedRequiredGlobalKeyAfterSpawn(SpawnData? critter) { if (!SpawnSystem.m_nospawn && critter != null && !((Object)(object)ZoneSystem.instance == (Object)null) && TryParseRequiredGlobalKeyThreshold(critter.m_requiredGlobalKey, out string key, out int amount) && amount > 0) { ZoneSystem.instance.SetGlobalKey(key + " --" + amount.ToString(CultureInfo.InvariantCulture)); } } private static bool TryParseRequiredGlobalKeyThreshold(string? rawValue, out string key, out int amount) { key = ""; amount = 0; if (string.IsNullOrWhiteSpace(rawValue)) { return false; } string text = rawValue.Trim(); GlobalKeys val = default(GlobalKeys); string text2 = default(string); key = ZoneSystem.GetKeyValue(text, ref text2, ref val); if (string.IsNullOrWhiteSpace(key) || string.IsNullOrWhiteSpace(text2)) { return false; } text2 = text2.Trim(); if (text2.StartsWith("++", StringComparison.OrdinalIgnoreCase) || text2.StartsWith("--", StringComparison.OrdinalIgnoreCase)) { return false; } return int.TryParse(text2, NumberStyles.Any, CultureInfo.InvariantCulture, out amount); } private static bool TryParseGlobalKeyMutation(string? rawValue, out string key, out int delta) { key = ""; delta = 0; if (string.IsNullOrWhiteSpace(rawValue)) { return false; } string text = rawValue.Trim(); GlobalKeys val = default(GlobalKeys); string text2 = default(string); key = ZoneSystem.GetKeyValue(text, ref text2, ref val); if (string.IsNullOrWhiteSpace(key) || string.IsNullOrWhiteSpace(text2)) { return false; } text2 = text2.Trim(); if (text2.StartsWith("++", StringComparison.OrdinalIgnoreCase)) { string text3 = text2; if (int.TryParse(text3.Substring(2, text3.Length - 2), NumberStyles.Any, CultureInfo.InvariantCulture, out var result)) { delta = result; return true; } } if (text2.StartsWith("--", StringComparison.OrdinalIgnoreCase)) { string text3 = text2; if (int.TryParse(text3.Substring(2, text3.Length - 2), NumberStyles.Any, CultureInfo.InvariantCulture, out var result2)) { delta = -result2; return true; } } return false; } private static void WarnInvalidEntry(string message) { InvalidEntryWarnings.Warn(message, requireSourceOfTruth: false, _capturedStrictValidationWarnings); } private static InvalidEntryDiagnostics.SuppressionScope BeginInvalidEntryWarningSuppressionForSyncedClientBuild(string sourceName) { return InvalidEntryWarnings.BeginSuppressionForSyncedClientBuild(sourceName); } private static bool HasAnySpawnFields(SpawnSystemSpawnDefinition? spawn) { if (spawn != null) { if (string.IsNullOrWhiteSpace(spawn.Name) && !spawn.HuntPlayer.HasValue) { IntRangeDefinition? levelRange = GetLevelRange(spawn); if ((levelRange == null || !levelRange.HasValues()) && !spawn.OverrideLevelUpChance.HasValue && !spawn.LevelUpMinCenterDistance.HasValue && !spawn.GroundOffset.HasValue && !spawn.GroundOffsetRandom.HasValue && !spawn.SpawnInterval.HasValue && !spawn.SpawnChance.HasValue) { FloatRangeDefinition? spawnRadiusRange = GetSpawnRadiusRange(spawn); if (spawnRadiusRange == null || !spawnRadiusRange.HasValues()) { IntRangeDefinition? groupSizeRange = GetGroupSizeRange(spawn); if ((groupSizeRange == null || !groupSizeRange.HasValues()) && !spawn.GroupRadius.HasValue && !spawn.NoSpawnRadius.HasValue && !spawn.MaxSpawned.HasValue && (spawn.Biomes?.Count ?? 0) <= 0 && (spawn.BiomeAreas?.Count ?? 0) <= 0 && string.IsNullOrWhiteSpace(spawn.RequiredGlobalKey) && (spawn.RequiredEnvironments?.Count ?? 0) <= 0 && spawn.TimeOfDay == null) { FloatRangeDefinition? altitudeRange = GetAltitudeRange(spawn); if (altitudeRange == null || !altitudeRange.HasValues()) { FloatRangeDefinition? tiltRange = GetTiltRange(spawn); if ((tiltRange == null || !tiltRange.HasValues()) && !spawn.InForest.HasValue && !spawn.InLava.HasValue && !spawn.CanSpawnCloseToPlayer.HasValue && !spawn.InsidePlayerBase.HasValue) { FloatRangeDefinition? oceanDepthRange = GetOceanDepthRange(spawn); if (oceanDepthRange == null || !oceanDepthRange.HasValues()) { FloatRangeDefinition? distanceFromCenterRange = GetDistanceFromCenterRange(spawn); if ((distanceFromCenterRange == null || !distanceFromCenterRange.HasValues()) && string.IsNullOrWhiteSpace(spawn.Data) && string.IsNullOrWhiteSpace(spawn.Faction) && (spawn.Fields?.Count ?? 0) <= 0) { return (spawn.Objects?.Count ?? 0) > 0; } } } } } } } } return true; } return false; } private static IntRangeDefinition? GetLevelRange(CanonicalSpawnSystemEntry entry) { return GetLevelRange(entry.SpawnSystem); } private static IntRangeDefinition? GetLevelRange(SpawnSystemSpawnDefinition? spawn) { return spawn?.Level ?? RangeFormatting.From(spawn?.MinLevel, spawn?.MaxLevel ?? spawn?.MinLevel); } private static FloatRangeDefinition? GetSpawnRadiusRange(CanonicalSpawnSystemEntry entry) { return GetSpawnRadiusRange(entry.SpawnSystem); } private static FloatRangeDefinition? GetSpawnRadiusRange(SpawnSystemSpawnDefinition? spawn) { return spawn?.SpawnRadius ?? RangeFormatting.From(spawn?.SpawnRadiusMin, spawn?.SpawnRadiusMax); } private static IntRangeDefinition? GetGroupSizeRange(CanonicalSpawnSystemEntry entry) { return GetGroupSizeRange(entry.SpawnSystem); } private static IntRangeDefinition? GetGroupSizeRange(SpawnSystemSpawnDefinition? spawn) { return spawn?.GroupSize ?? RangeFormatting.From(spawn?.GroupSizeMin, spawn?.GroupSizeMax ?? spawn?.GroupSizeMin); } private static FloatRangeDefinition? GetAltitudeRange(CanonicalSpawnSystemEntry entry) { return GetAltitudeRange(entry.SpawnSystem); } private static FloatRangeDefinition? GetAltitudeRange(SpawnSystemSpawnDefinition? conditions) { return conditions?.Altitude ?? RangeFormatting.From(conditions?.MinAltitude, conditions?.MaxAltitude); } private static FloatRangeDefinition? GetTiltRange(CanonicalSpawnSystemEntry entry) { return GetTiltRange(entry.SpawnSystem); } private static FloatRangeDefinition? GetTiltRange(SpawnSystemSpawnDefinition? conditions) { return conditions?.Tilt ?? RangeFormatting.From(conditions?.MinTilt, conditions?.MaxTilt); } private static FloatRangeDefinition? GetOceanDepthRange(CanonicalSpawnSystemEntry entry) { return GetOceanDepthRange(entry.SpawnSystem); } private static FloatRangeDefinition? GetOceanDepthRange(SpawnSystemSpawnDefinition? conditions) { return conditions?.OceanDepth ?? RangeFormatting.From(conditions?.MinOceanDepth, conditions?.MaxOceanDepth); } private static FloatRangeDefinition? GetDistanceFromCenterRange(CanonicalSpawnSystemEntry entry) { return GetDistanceFromCenterRange(entry.SpawnSystem); } private static FloatRangeDefinition? GetDistanceFromCenterRange(SpawnSystemSpawnDefinition? conditions) { return conditions?.DistanceFromCenter ?? RangeFormatting.From(conditions?.MinDistanceFromCenter, conditions?.MaxDistanceFromCenter); } private static List> BuildBiomeOrderedSnapshotSections(SpawnSystemSnapshot snapshot) { List> list = PrefabOutputSections.BuildSections(snapshot.Entries, (SpawnSystemEntrySnapshot entry) => NormalizeReferencePrefabName(entry.Data.m_prefab) ?? ""); foreach (PrefabOwnerSection item in list) { item.Entries.Sort(CompareSpawnSystemEntriesForOutput); } return list; } private static List> BuildBiomeOrderedReferenceSections(IEnumerable entries) { PrefabOwnerResolver.OwnerSnapshot ownerSnapshot = PrefabOwnerResolver.GetSnapshot(); List> list = PrefabOutputSections.BuildSections(entries, (CanonicalSpawnSystemEntry entry) => (entry.Prefab ?? "").Trim(), (CanonicalSpawnSystemEntry entry) => string.IsNullOrWhiteSpace(entry.ReferenceOwnerName) ? ownerSnapshot.GetOwnerName((entry.Prefab ?? "").Trim()) : entry.ReferenceOwnerName); foreach (PrefabOwnerSection item in list) { item.Entries.Sort(CompareReferenceEntriesForOutput); } return list; } private static CanonicalSpawnSystemEntry ConvertToReferenceEntry(SpawnSystemEntrySnapshot snapshot) { return ConvertToReferenceEntry(snapshot.Data); } private static CanonicalSpawnSystemEntry ConvertToReferenceEntry(SpawnData data) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Expected O, but got Unknown //IL_015a: Unknown result type (might be due to invalid IL or missing references) //IL_0160: 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_017c: Unknown result type (might be due to invalid IL or missing references) //IL_0182: Unknown result type (might be due to invalid IL or missing references) //IL_018a: Unknown result type (might be due to invalid IL or missing references) SpawnData val = new SpawnData(); string text = NormalizeReferencePrefabName(data.m_prefab); string text2 = NormalizeNullable(data.m_name); SpawnSystemSpawnDefinition spawnSystemSpawnDefinition = new SpawnSystemSpawnDefinition { Name = (string.Equals(text2, text, StringComparison.OrdinalIgnoreCase) ? null : text2), Level = RangeFormatting.FromReference(data.m_minLevel, data.m_maxLevel, val.m_minLevel, val.m_maxLevel), LevelUpMinCenterDistance = GetReferenceFloatOrNull(data.m_levelUpMinCenterDistance, val.m_levelUpMinCenterDistance), OverrideLevelUpChance = GetReferenceFloatOrNull(data.m_overrideLevelupChance, val.m_overrideLevelupChance), SpawnInterval = GetReferenceFloatOrNull(data.m_spawnInterval, val.m_spawnInterval), SpawnChance = GetReferenceFloatOrNull(data.m_spawnChance, val.m_spawnChance), SpawnRadius = RangeFormatting.FromReference(data.m_spawnRadiusMin, data.m_spawnRadiusMax, val.m_spawnRadiusMin, val.m_spawnRadiusMax), GroupSize = RangeFormatting.FromReference(data.m_groupSizeMin, data.m_groupSizeMax, val.m_groupSizeMin, val.m_groupSizeMax), GroupRadius = GetReferenceFloatOrNull(data.m_groupRadius, val.m_groupRadius), HuntPlayer = GetReferenceBoolOrNull(data.m_huntPlayer, val.m_huntPlayer), GroundOffset = GetReferenceFloatOrNull(data.m_groundOffset, val.m_groundOffset), GroundOffsetRandom = GetReferenceFloatOrNull(data.m_groundOffsetRandom, val.m_groundOffsetRandom), Biomes = ((data.m_biome == val.m_biome) ? null : ConvertBiomes(data.m_biome)), BiomeAreas = ((data.m_biomeArea == val.m_biomeArea) ? null : ConvertBiomeAreas(data.m_biomeArea)), RequiredGlobalKey = NormalizeNullable(data.m_requiredGlobalKey), RequiredEnvironments = NormalizeReferenceStringList(data.m_requiredEnvironments), TimeOfDay = ((data.m_spawnAtDay == val.m_spawnAtDay && data.m_spawnAtNight == val.m_spawnAtNight) ? null : TimeOfDayFormatting.FromSpawnFlags(data.m_spawnAtDay, data.m_spawnAtNight)), NoSpawnRadius = GetReferenceFloatOrNull(data.m_spawnDistance, val.m_spawnDistance), MaxSpawned = GetReferenceIntOrNull(data.m_maxSpawned, val.m_maxSpawned), Altitude = RangeFormatting.FromReference(data.m_minAltitude, data.m_maxAltitude, val.m_minAltitude, val.m_maxAltitude), Tilt = RangeFormatting.FromReference(data.m_minTilt, data.m_maxTilt, val.m_minTilt, val.m_maxTilt), InForest = GetReferenceExclusiveZoneToggle(data.m_inForest, data.m_outsideForest, val.m_inForest, val.m_outsideForest), InLava = GetReferenceExclusiveZoneToggle(data.m_inLava, data.m_outsideLava, val.m_inLava, val.m_outsideLava), CanSpawnCloseToPlayer = GetReferenceBoolOrNull(data.m_canSpawnCloseToPlayer, val.m_canSpawnCloseToPlayer), InsidePlayerBase = GetReferenceBoolOrNull(data.m_insidePlayerBase, val.m_insidePlayerBase), OceanDepth = RangeFormatting.FromReference(data.m_minOceanDepth, data.m_maxOceanDepth, val.m_minOceanDepth, val.m_maxOceanDepth), DistanceFromCenter = RangeFormatting.FromReference(data.m_minDistanceFromCenter, data.m_maxDistanceFromCenter, val.m_minDistanceFromCenter, val.m_maxDistanceFromCenter) }; return new CanonicalSpawnSystemEntry { Prefab = text, Enabled = data.m_enabled, SpawnSystem = (HasAnySpawnFields(spawnSystemSpawnDefinition) ? spawnSystemSpawnDefinition : null) }; } internal static CanonicalSpawnSystemEntry CreateReferenceEntryForExternalProjection(SpawnData data, string? prefabNameFallback = null) { CanonicalSpawnSystemEntry canonicalSpawnSystemEntry = ConvertToReferenceEntry(data); if (string.IsNullOrWhiteSpace(canonicalSpawnSystemEntry.Prefab)) { canonicalSpawnSystemEntry.Prefab = NormalizeNullable(prefabNameFallback); } return canonicalSpawnSystemEntry; } private static CanonicalSpawnSystemEntry ConvertToConfigurationEntry(SpawnSystemEntrySnapshot snapshot) { //IL_0153: 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) SpawnSystemSpawnDefinition spawnSystem = new SpawnSystemSpawnDefinition { Name = NormalizeNullable(snapshot.Data.m_name), HuntPlayer = snapshot.Data.m_huntPlayer, Level = RangeFormatting.From(snapshot.Data.m_minLevel, snapshot.Data.m_maxLevel), LevelUpMinCenterDistance = snapshot.Data.m_levelUpMinCenterDistance, OverrideLevelUpChance = snapshot.Data.m_overrideLevelupChance, SpawnInterval = snapshot.Data.m_spawnInterval, SpawnChance = snapshot.Data.m_spawnChance, SpawnRadius = RangeFormatting.From(snapshot.Data.m_spawnRadiusMin, snapshot.Data.m_spawnRadiusMax), GroupSize = RangeFormatting.From(snapshot.Data.m_groupSizeMin, snapshot.Data.m_groupSizeMax), GroupRadius = snapshot.Data.m_groupRadius, GroundOffset = snapshot.Data.m_groundOffset, GroundOffsetRandom = snapshot.Data.m_groundOffsetRandom, Biomes = ConvertBiomes(snapshot.Data.m_biome), BiomeAreas = ConvertBiomeAreas(snapshot.Data.m_biomeArea), RequiredGlobalKey = NormalizeNullable(snapshot.Data.m_requiredGlobalKey), RequiredEnvironments = snapshot.Data.m_requiredEnvironments.Select((string environment) => environment.Trim()).ToList(), TimeOfDay = TimeOfDayFormatting.FromSpawnFlags(snapshot.Data.m_spawnAtDay, snapshot.Data.m_spawnAtNight), NoSpawnRadius = snapshot.Data.m_spawnDistance, MaxSpawned = snapshot.Data.m_maxSpawned, Altitude = RangeFormatting.From(snapshot.Data.m_minAltitude, snapshot.Data.m_maxAltitude), Tilt = RangeFormatting.From(snapshot.Data.m_minTilt, snapshot.Data.m_maxTilt), InForest = ConvertExclusiveZoneToggle(snapshot.Data.m_inForest, snapshot.Data.m_outsideForest), InLava = ConvertExclusiveZoneToggle(snapshot.Data.m_inLava, snapshot.Data.m_outsideLava), CanSpawnCloseToPlayer = snapshot.Data.m_canSpawnCloseToPlayer, InsidePlayerBase = snapshot.Data.m_insidePlayerBase, OceanDepth = RangeFormatting.From(snapshot.Data.m_minOceanDepth, snapshot.Data.m_maxOceanDepth), DistanceFromCenter = RangeFormatting.From(snapshot.Data.m_minDistanceFromCenter, snapshot.Data.m_maxDistanceFromCenter) }; return new CanonicalSpawnSystemEntry { Enabled = snapshot.Data.m_enabled, Prefab = NormalizeReferencePrefabName(snapshot.Data.m_prefab), SpawnSystem = spawnSystem }; } private static int CompareSpawnSystemEntriesForOutput(SpawnSystemEntrySnapshot left, SpawnSystemEntrySnapshot right) { (int, string, int, int) biomeSortKey = GetBiomeSortKey(left); (int, string, int, int) biomeSortKey2 = GetBiomeSortKey(right); int num = biomeSortKey.Item1.CompareTo(biomeSortKey2.Item1); if (num != 0) { return num; } num = StringComparer.OrdinalIgnoreCase.Compare(biomeSortKey.Item2, biomeSortKey2.Item2); if (num != 0) { return num; } num = biomeSortKey.Item3.CompareTo(biomeSortKey2.Item3); if (num != 0) { return num; } num = biomeSortKey2.Item4.CompareTo(biomeSortKey.Item4); if (num != 0) { return num; } num = StringComparer.OrdinalIgnoreCase.Compare(NormalizeReferencePrefabName(left.Data.m_prefab) ?? "", NormalizeReferencePrefabName(right.Data.m_prefab) ?? ""); if (num != 0) { return num; } num = StringComparer.OrdinalIgnoreCase.Compare(left.Data.m_name ?? "", right.Data.m_name ?? ""); if (num != 0) { return num; } return StringComparer.Ordinal.Compare(GetStableReferenceSortKey(left), GetStableReferenceSortKey(right)); } private static string GetStableReferenceSortKey(SpawnSystemEntrySnapshot snapshot) { return GetStableReferenceSortKey(ConvertToReferenceEntry(snapshot)); } private static string GetStableReferenceSortKey(CanonicalSpawnSystemEntry entry) { return Serializer.Serialize(entry).TrimEnd('\r', '\n'); } private static (int GroupRank, string GroupName, int EarliestRank, int BiomeCount) GetBiomeSortKey(SpawnSystemEntrySnapshot snapshot) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) return BuildBiomeSortKey(ConvertBiomes(snapshot.Data.m_biome)); } private static (int GroupRank, string GroupName, int EarliestRank, int BiomeCount) GetBiomeSortKey(CanonicalSpawnSystemEntry entry) { return BuildBiomeSortKey(entry.SpawnSystem?.Biomes); } private static int CompareReferenceEntriesForOutput(CanonicalSpawnSystemEntry left, CanonicalSpawnSystemEntry right) { (int, string, int, int) biomeSortKey = GetBiomeSortKey(left); (int, string, int, int) biomeSortKey2 = GetBiomeSortKey(right); int num = biomeSortKey.Item1.CompareTo(biomeSortKey2.Item1); if (num != 0) { return num; } num = StringComparer.OrdinalIgnoreCase.Compare(biomeSortKey.Item2, biomeSortKey2.Item2); if (num != 0) { return num; } num = biomeSortKey.Item3.CompareTo(biomeSortKey2.Item3); if (num != 0) { return num; } num = biomeSortKey2.Item4.CompareTo(biomeSortKey.Item4); if (num != 0) { return num; } num = StringComparer.OrdinalIgnoreCase.Compare(left.Prefab ?? "", right.Prefab ?? ""); if (num != 0) { return num; } num = StringComparer.OrdinalIgnoreCase.Compare(left.SpawnSystem?.Name ?? "", right.SpawnSystem?.Name ?? ""); if (num != 0) { return num; } return StringComparer.Ordinal.Compare(GetStableReferenceSortKey(left), GetStableReferenceSortKey(right)); } private static (int GroupRank, string GroupName, int EarliestRank, int BiomeCount) BuildBiomeSortKey(List? biomes) { List list = (from text2 in biomes ?? new List() select (text2 ?? "").Trim() into text2 where text2.Length > 0 select text2).ToList(); if (list.Count == 0) { return (GroupRank: int.MaxValue, GroupName: "", EarliestRank: int.MaxValue, BiomeCount: 0); } bool flag = false; int num = -1; int num2 = int.MaxValue; foreach (string item2 in list) { string text = NormalizeBiomeSortToken(item2); (string, int) value; if (text == "all") { flag = true; } else if (BiomeOutputOrderLookup.TryGetValue(text, out value)) { num = Math.Max(num, value.Item2); num2 = Math.Min(num2, value.Item2); } } if (flag) { num = Math.Max(num, BiomeOutputOrder.Length - 1); num2 = Math.Min(num2, 0); } if (num >= 0) { return (GroupRank: num, GroupName: BiomeOutputOrder[num], EarliestRank: num2, BiomeCount: list.Count); } string item = list.OrderBy((string result) => result, StringComparer.OrdinalIgnoreCase).First(); return (GroupRank: BiomeOutputOrder.Length + 1, GroupName: item, EarliestRank: int.MaxValue, BiomeCount: list.Count); } private static Dictionary BuildBiomeOutputOrderLookup() { Dictionary dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); for (int i = 0; i < BiomeOutputOrder.Length; i++) { string text = BiomeOutputOrder[i]; dictionary[NormalizeBiomeSortToken(text)] = (text, i); } dictionary["ashlands"] = ("AshLands", Array.IndexOf(BiomeOutputOrder, "AshLands")); return dictionary; } private static string NormalizeBiomeSortToken(string? value) { return BiomeResolutionSupport.NormalizeBiomeToken(value); } private static bool HasAnySpawnFields(CanonicalSpawnSystemEntry entry) { return HasAnySpawnFields(entry.SpawnSystem); } private static List? NormalizeReferenceStringList(IEnumerable? values) { if (values == null) { return null; } List list = (from value in values select (value ?? "").Trim() into value where value.Length > 0 select value).ToList(); if (list.Count <= 0) { return null; } return list; } private static bool? GetReferenceBoolOrNull(bool value, bool defaultValue) { if (value != defaultValue) { return value; } return null; } private static bool? ConvertExclusiveZoneToggle(bool allowInside, bool allowOutside) { if (allowInside && !allowOutside) { return true; } if (!allowInside && allowOutside) { return false; } return null; } private static bool? GetReferenceExclusiveZoneToggle(bool allowInside, bool allowOutside, bool defaultAllowInside, bool defaultAllowOutside) { bool? flag = ConvertExclusiveZoneToggle(allowInside, allowOutside); if (flag != ConvertExclusiveZoneToggle(defaultAllowInside, defaultAllowOutside)) { return flag; } return null; } private static void ApplyExclusiveZoneToggle(bool? value, ref bool allowInside, ref bool allowOutside) { if (value.HasValue) { allowInside = value.Value; allowOutside = !value.Value; } } private static int? GetReferenceIntOrNull(int value, int defaultValue) { if (value != defaultValue) { return value; } return null; } private static float? GetReferenceFloatOrNull(float value, float defaultValue) { if (!(Math.Abs(value - defaultValue) < 0.0001f)) { return value; } return null; } private static void AppendReferenceEntry(StringBuilder builder, CanonicalSpawnSystemEntry entry) { //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Expected O, but got Unknown AppendYamlListEntryLine(builder, 0, "prefab", entry.Prefab); if (!entry.Enabled) { AppendYamlOptionalBoolLine(builder, 1, "enabled", false); } AppendYamlSpawnSystemPayloadBlock(builder, 1, entry, new SpawnData(), includeEmptyPlaceholder: false); } private static void AppendConfigurationEntry(StringBuilder builder, CanonicalSpawnSystemEntry entry) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Expected O, but got Unknown SpawnData defaults = new SpawnData(); AppendYamlListEntryLine(builder, 0, "prefab", entry.Prefab); AppendYamlLine(builder, 1, "enabled: " + FormatYamlBool(entry.Enabled)); AppendYamlSpawnSystemPayloadBlock(builder, 1, entry, defaults, includeEmptyPlaceholder: true); } internal static void AppendYamlSpawnSystemPayloadBlock(StringBuilder builder, int indent, CanonicalSpawnSystemEntry entry, SpawnData defaults, bool includeEmptyPlaceholder) { if (includeEmptyPlaceholder || HasAnySpawnFields(entry)) { AppendYamlLine(builder, indent, "spawnSystem:"); AppendYamlSpawnSystemSpawnBlock(builder, indent + 1, entry, defaults, includeEmptyPlaceholder); } } private static void AppendYamlSpawnSystemSpawnBlock(StringBuilder builder, int indent, CanonicalSpawnSystemEntry entry, SpawnData defaults, bool includeEmptyPlaceholder) { SpawnSystemSpawnDefinition spawnSystem = entry.SpawnSystem; if (includeEmptyPlaceholder || HasAnySpawnFields(spawnSystem)) { TimeOfDayDefinition fallback = TimeOfDayFormatting.FromSpawnFlags(defaults.m_spawnAtDay, defaults.m_spawnAtNight); if (includeEmptyPlaceholder) { AppendYamlStringLine(builder, indent, "name", spawnSystem?.Name); AppendYamlLine(builder, indent, "huntPlayer: " + FormatYamlBool(spawnSystem?.HuntPlayer ?? defaults.m_huntPlayer)); AppendYamlLine(builder, indent, "level: " + RangeFormatting.FormatInlineObject(GetLevelRange(entry) ?? RangeFormatting.From(defaults.m_minLevel, defaults.m_maxLevel))); AppendYamlLine(builder, indent, "overrideLevelUpChance: " + FormatYamlFloat(spawnSystem?.OverrideLevelUpChance ?? defaults.m_overrideLevelupChance)); AppendYamlLine(builder, indent, "levelUpMinCenterDistance: " + FormatYamlFloat(spawnSystem?.LevelUpMinCenterDistance ?? defaults.m_levelUpMinCenterDistance)); AppendYamlLine(builder, indent, "groundOffset: " + FormatYamlFloat(spawnSystem?.GroundOffset ?? defaults.m_groundOffset)); AppendYamlLine(builder, indent, "groundOffsetRandom: " + FormatYamlFloat(spawnSystem?.GroundOffsetRandom ?? defaults.m_groundOffsetRandom)); AppendYamlLine(builder, indent, "spawnInterval: " + FormatYamlFloat(spawnSystem?.SpawnInterval ?? defaults.m_spawnInterval)); AppendYamlLine(builder, indent, "spawnChance: " + FormatYamlFloat(spawnSystem?.SpawnChance ?? defaults.m_spawnChance)); FloatRangeDefinition floatRangeDefinition = GetSpawnRadiusRange(entry) ?? RangeFormatting.From(defaults.m_spawnRadiusMin, defaults.m_spawnRadiusMax); float valueOrDefault = (floatRangeDefinition?.Min).GetValueOrDefault(); float valueOrDefault2 = (floatRangeDefinition?.Max).GetValueOrDefault(); floatRangeDefinition = RangeFormatting.From((valueOrDefault > 0f) ? valueOrDefault : 40f, (valueOrDefault2 > 0f) ? valueOrDefault2 : 80f); AppendYamlLine(builder, indent, "spawnRadius: " + RangeFormatting.FormatInlineObject(floatRangeDefinition)); AppendYamlLine(builder, indent, "groupSize: " + RangeFormatting.FormatInlineObject(GetGroupSizeRange(entry) ?? RangeFormatting.From(defaults.m_groupSizeMin, defaults.m_groupSizeMax))); AppendYamlLine(builder, indent, "groupRadius: " + FormatYamlFloat(spawnSystem?.GroupRadius ?? defaults.m_groupRadius)); AppendYamlLine(builder, indent, "noSpawnRadius: " + FormatYamlFloat(spawnSystem?.NoSpawnRadius ?? defaults.m_spawnDistance)); AppendYamlLine(builder, indent, $"maxSpawned: {spawnSystem?.MaxSpawned ?? defaults.m_maxSpawned}"); AppendYamlLine(builder, indent, "tilt: " + RangeFormatting.FormatInlineObject(GetTiltRange(entry) ?? RangeFormatting.From(defaults.m_minTilt, defaults.m_maxTilt))); AppendYamlLine(builder, indent, "altitude: " + RangeFormatting.FormatInlineObject(GetAltitudeRange(entry) ?? RangeFormatting.From(defaults.m_minAltitude, defaults.m_maxAltitude))); AppendYamlLine(builder, indent, "oceanDepth: " + RangeFormatting.FormatInlineObject(GetOceanDepthRange(entry) ?? RangeFormatting.From(defaults.m_minOceanDepth, defaults.m_maxOceanDepth))); AppendYamlLine(builder, indent, "distanceFromCenter: " + RangeFormatting.FormatInlineObject(GetDistanceFromCenterRange(entry) ?? RangeFormatting.From(defaults.m_minDistanceFromCenter, defaults.m_maxDistanceFromCenter))); AppendYamlConditionalInlineListLine(builder, indent, "biomes", spawnSystem?.Biomes, includeEmptyPlaceholder); AppendYamlConditionalInlineListLine(builder, indent, "biomeAreas", spawnSystem?.BiomeAreas, includeEmptyPlaceholder); AppendYamlLine(builder, indent, "timeOfDay: " + TimeOfDayFormatting.FormatInlineList(spawnSystem?.TimeOfDay, fallback)); AppendYamlConditionalInlineListLine(builder, indent, "requiredEnvironments", spawnSystem?.RequiredEnvironments, includeEmptyPlaceholder); AppendYamlStringLine(builder, indent, "requiredGlobalKey", spawnSystem?.RequiredGlobalKey ?? defaults.m_requiredGlobalKey); AppendYamlLine(builder, indent, "inLava: " + FormatYamlNullableBoolOrNull(spawnSystem?.InLava)); AppendYamlLine(builder, indent, "inForest: " + FormatYamlNullableBoolOrNull(spawnSystem?.InForest)); AppendYamlLine(builder, indent, "insidePlayerBase: " + FormatYamlBool(spawnSystem?.InsidePlayerBase ?? defaults.m_insidePlayerBase)); AppendYamlLine(builder, indent, "canSpawnCloseToPlayer: " + FormatYamlBool(spawnSystem?.CanSpawnCloseToPlayer ?? defaults.m_canSpawnCloseToPlayer)); AppendYamlDictionaryLine(builder, indent, "fields", spawnSystem?.Fields); AppendYamlInlineListLine(builder, indent, "objects", spawnSystem?.Objects); AppendYamlStringLine(builder, indent, "data", spawnSystem?.Data); AppendYamlStringLine(builder, indent, "faction", spawnSystem?.Faction); } else { AppendYamlOptionalStringLine(builder, indent, "name", spawnSystem?.Name); AppendYamlOptionalBoolLine(builder, indent, "huntPlayer", spawnSystem?.HuntPlayer); AppendYamlOptionalRangeLine(builder, indent, "level", GetLevelRange(entry)); AppendYamlOptionalFloatLine(builder, indent, "overrideLevelUpChance", spawnSystem?.OverrideLevelUpChance); AppendYamlOptionalFloatLine(builder, indent, "levelUpMinCenterDistance", spawnSystem?.LevelUpMinCenterDistance); AppendYamlOptionalFloatLine(builder, indent, "groundOffset", spawnSystem?.GroundOffset); AppendYamlOptionalFloatLine(builder, indent, "groundOffsetRandom", spawnSystem?.GroundOffsetRandom); AppendYamlOptionalFloatLine(builder, indent, "spawnInterval", spawnSystem?.SpawnInterval); AppendYamlOptionalFloatLine(builder, indent, "spawnChance", spawnSystem?.SpawnChance); AppendYamlOptionalRangeLine(builder, indent, "spawnRadius", GetSpawnRadiusRange(entry)); AppendYamlOptionalRangeLine(builder, indent, "groupSize", GetGroupSizeRange(entry)); AppendYamlOptionalFloatLine(builder, indent, "groupRadius", spawnSystem?.GroupRadius); AppendYamlOptionalFloatLine(builder, indent, "noSpawnRadius", spawnSystem?.NoSpawnRadius); AppendYamlOptionalIntLine(builder, indent, "maxSpawned", spawnSystem?.MaxSpawned); AppendYamlOptionalRangeLine(builder, indent, "tilt", GetTiltRange(entry)); AppendYamlOptionalRangeLine(builder, indent, "altitude", GetAltitudeRange(entry)); AppendYamlOptionalRangeLine(builder, indent, "oceanDepth", GetOceanDepthRange(entry)); AppendYamlOptionalRangeLine(builder, indent, "distanceFromCenter", GetDistanceFromCenterRange(entry)); AppendYamlOptionalInlineListLine(builder, indent, "biomes", spawnSystem?.Biomes); AppendYamlOptionalInlineListLine(builder, indent, "biomeAreas", spawnSystem?.BiomeAreas); AppendYamlOptionalTimeOfDayLine(builder, indent, "timeOfDay", spawnSystem?.TimeOfDay); AppendYamlOptionalInlineListLine(builder, indent, "requiredEnvironments", spawnSystem?.RequiredEnvironments); AppendYamlOptionalStringLine(builder, indent, "requiredGlobalKey", spawnSystem?.RequiredGlobalKey); AppendYamlOptionalBoolLine(builder, indent, "inLava", spawnSystem?.InLava); AppendYamlOptionalBoolLine(builder, indent, "inForest", spawnSystem?.InForest); AppendYamlOptionalBoolLine(builder, indent, "insidePlayerBase", spawnSystem?.InsidePlayerBase); AppendYamlOptionalBoolLine(builder, indent, "canSpawnCloseToPlayer", spawnSystem?.CanSpawnCloseToPlayer); AppendYamlOptionalDictionaryLine(builder, indent, "fields", spawnSystem?.Fields); AppendYamlOptionalInlineListLine(builder, indent, "objects", spawnSystem?.Objects); AppendYamlOptionalStringLine(builder, indent, "data", spawnSystem?.Data); AppendYamlOptionalStringLine(builder, indent, "faction", spawnSystem?.Faction); } } } private static void AppendYamlLine(StringBuilder builder, int indent, string text) { builder.Append(' ', indent * 2); builder.AppendLine(text); } private static void AppendYamlListEntryLine(StringBuilder builder, int indent, string key, string? value) { builder.Append(' ', indent * 2); builder.Append("- ").Append(key).Append(": ") .AppendLine(FormatYamlString(value)); } private static void AppendYamlStringLine(StringBuilder builder, int indent, string key, string? value) { builder.Append(' ', indent * 2); builder.Append(key).Append(": "); if (value == null) { builder.Append("null"); } else { builder.Append(FormatYamlString(value)); } builder.AppendLine(); } private static void AppendYamlOptionalStringLine(StringBuilder builder, int indent, string key, string? value) { if (!string.IsNullOrWhiteSpace(value)) { AppendYamlStringLine(builder, indent, key, value); } } private static void AppendYamlInlineListLine(StringBuilder builder, int indent, string key, List? values) { builder.Append(' ', indent * 2); builder.Append(key).Append(": ").AppendLine(FormatYamlInlineList(values)); } private static void AppendYamlOptionalInlineListLine(StringBuilder builder, int indent, string key, List? values) { if (values != null && values.Count > 0) { AppendYamlInlineListLine(builder, indent, key, values); } } private static void AppendYamlConditionalInlineListLine(StringBuilder builder, int indent, string key, List? values, bool includeEmptyPlaceholder) { if (includeEmptyPlaceholder || (values?.Count ?? 0) > 0) { AppendYamlInlineListLine(builder, indent, key, values); } } private static void AppendYamlOptionalTimeOfDayLine(StringBuilder builder, int indent, string key, TimeOfDayDefinition? value) { if (value != null) { AppendYamlLine(builder, indent, key + ": " + TimeOfDayFormatting.FormatInlineList(value)); } } private static void AppendYamlOptionalBoolLine(StringBuilder builder, int indent, string key, bool? value) { if (value.HasValue) { AppendYamlLine(builder, indent, key + ": " + FormatYamlBool(value.Value)); } } private static void AppendYamlOptionalIntLine(StringBuilder builder, int indent, string key, int? value) { if (value.HasValue) { AppendYamlLine(builder, indent, $"{key}: {value.Value}"); } } private static void AppendYamlOptionalFloatLine(StringBuilder builder, int indent, string key, float? value) { if (value.HasValue) { AppendYamlLine(builder, indent, key + ": " + FormatYamlFloat(value.Value)); } } private static void AppendYamlOptionalRangeLine(StringBuilder builder, int indent, string key, IntRangeDefinition? range) { if (range != null && range.HasValues()) { AppendYamlLine(builder, indent, key + ": " + RangeFormatting.FormatShorthand(range)); } } private static void AppendYamlOptionalRangeLine(StringBuilder builder, int indent, string key, FloatRangeDefinition? range) { if (range != null && range.HasValues()) { AppendYamlLine(builder, indent, key + ": " + RangeFormatting.FormatShorthand(range)); } } private static void AppendYamlDictionaryLine(StringBuilder builder, int indent, string key, Dictionary? values) { if (values == null || values.Count == 0) { AppendYamlLine(builder, indent, key + ": {}"); return; } builder.Append(' ', indent * 2); builder.Append(key).Append(": { "); builder.Append(string.Join(", ", values.Select, string>((KeyValuePair pair) => FormatYamlString(pair.Key) + ": " + FormatYamlString(pair.Value)))); builder.AppendLine(" }"); } private static void AppendYamlOptionalDictionaryLine(StringBuilder builder, int indent, string key, Dictionary? values) { if (values != null && values.Count > 0) { AppendYamlDictionaryLine(builder, indent, key, values); } } private static string FormatYamlInlineList(List? values) { if (values == null || values.Count == 0) { return "[]"; } return "[" + string.Join(", ", values.Select(FormatYamlString)) + "]"; } private static string FormatYamlString(string? value) { string graph = value ?? ""; return Serializer.Serialize(graph).TrimEnd('\r', '\n'); } private static string FormatYamlBool(bool value) { if (!value) { return "false"; } return "true"; } private static string FormatYamlNullableBoolOrNull(bool? value) { if (!value.HasValue) { return "null"; } return FormatYamlBool(value.Value); } private static string FormatYamlFloat(float value) { if (!(Math.Abs(value % 1f) < 0.0001f)) { return value.ToString("0.###", CultureInfo.InvariantCulture); } return ((int)MathF.Round(value)).ToString(CultureInfo.InvariantCulture); } private static void QueueLiveSystemAttachForTable(CompiledSpawnSystemTable? table, int buildVersion, bool queueEspRefresh, IEnumerable? systems = null) { if (table == null) { return; } foreach (SpawnSystem item in systems ?? GetLiveSystems()) { QueueLiveSystemAttach(item, table, buildVersion, queueEspRefresh); } } private static void QueueLiveSystemAttach(SpawnSystem? system, CompiledSpawnSystemTable targetTable, int buildVersion, bool queueEspRefresh) { if ((Object)(object)system == (Object)null) { return; } int instanceID = ((Object)system).GetInstanceID(); if (!PendingLiveSystemAttachIds.Add(instanceID)) { if (queueEspRefresh) { PendingLiveSystemAttachEspRefreshIds.Add(instanceID); } return; } if (queueEspRefresh) { PendingLiveSystemAttachEspRefreshIds.Add(instanceID); } PendingLiveSystemAttaches.Enqueue(new PendingLiveSystemAttach(system, instanceID, _reconcileQueueEpoch, buildVersion, targetTable)); } private static void AttachTableToSystem(SpawnSystem? system, CompiledSpawnSystemTable? table) { if (!((Object)(object)system == (Object)null) && table != null && table.Lists.Count != 0) { ClearAttachedRuntimeState(system); SnapshotsBySystemId.Remove(((Object)system).GetInstanceID()); _templateSnapshot = null; system.m_spawnLists = CloneAttachedSpawnLists(table); } } private static bool IsSystemAttachedToCompiledTable(SpawnSystem? system, CompiledSpawnSystemTable? table) { if ((Object)(object)system == (Object)null || table == null || table.Lists.Count == 0) { return false; } SpawnListSummary spawnListSummary = SummarizeSpawnLists(system.m_spawnLists); int num = ((table.BaselineListCount > 0) ? table.BaselineListCount : table.Lists.Count); int num2 = ((table.BaselineListCount > 0 || table.BaselineRowCount > 0) ? table.BaselineRowCount : CountSpawnRows(table)); int num3 = ((table.BaselineContentHash != 0) ? table.BaselineContentHash : SummarizeSpawnLists(table.Lists).ContentHash); if (spawnListSummary.ListCount == num && spawnListSummary.RowCount == num2) { return spawnListSummary.ContentHash == num3; } return false; } private static List CloneAttachedSpawnLists(CompiledSpawnSystemTable table) { List list = new List(table.Lists.Count); foreach (SpawnSystemList list3 in table.Lists) { List list2 = new List((list3?.m_spawners?.Count).GetValueOrDefault()); if (list3?.m_spawners != null) { foreach (SpawnData spawner in list3.m_spawners) { if (spawner != null) { SpawnData val = spawner.Clone(); table.RuntimeTimeOfDayBySpawnData.TryGetValue(spawner, out TimeOfDayDefinition value); table.CustomPayloadsBySpawnData.TryGetValue(spawner, out SpawnSystemCustomDataSupport.PreparedPayload value2); SpawnSystemCustomDataSupport.ApplyPreparedPayload(val, value2); ApplyRuntimeMetadata(val, value); list2.Add(val); } } } list.Add(CreateAttachedSpawnList(list2)); } return list; } private static CompiledSpawnSystemTable? GetSelectedCompiledTableForCurrentState() { if (!PluginSettingsFacade.IsSpawnSystemDomainEnabled()) { return _vanillaCompiledTable; } return _activeCompiledTable ?? _vanillaCompiledTable; } private static CompiledSpawnSystemTable? BuildVanillaCompiledTable(int gameDataSignature) { bool referenceSourceTrusted; List vanillaSourceSpawnLists = GetVanillaSourceSpawnLists(out referenceSourceTrusted); if (vanillaSourceSpawnLists.Count == 0) { return null; } CompiledSpawnSystemTable compiledSpawnSystemTable = new CompiledSpawnSystemTable { GameDataSignature = gameDataSignature, Signature = "vanilla:" + gameDataSignature.ToString(CultureInfo.InvariantCulture), ReferenceSourceTrusted = referenceSourceTrusted }; foreach (SpawnSystemList item in vanillaSourceSpawnLists) { List spawners = (from spawnData in item.m_spawners where spawnData != null select spawnData.Clone()).ToList(); compiledSpawnSystemTable.Lists.Add(CreateManagedSpawnList(spawners)); } FreezeCompiledTableBaseline(compiledSpawnSystemTable); return compiledSpawnSystemTable; } private static List GetVanillaSourceSpawnLists(out bool referenceSourceTrusted) { referenceSourceTrusted = false; SpawnSystem zoneCtrlPrefabSpawnSystem = GetZoneCtrlPrefabSpawnSystem(); if ((Object)(object)zoneCtrlPrefabSpawnSystem != (Object)null) { referenceSourceTrusted = true; return zoneCtrlPrefabSpawnSystem.m_spawnLists.Where((SpawnSystemList spawnList) => (Object)(object)spawnList != (Object)null).ToList(); } SpawnSystem val = GetLiveSystems().FirstOrDefault(); if ((Object)(object)val == (Object)null) { return new List(); } return val.m_spawnLists.Where((SpawnSystemList spawnList) => (Object)(object)spawnList != (Object)null).ToList(); } private static SpawnSystem? GetZoneCtrlPrefabSpawnSystem() { if ((Object)(object)ZoneSystem.instance?.m_zoneCtrlPrefab == (Object)null) { return null; } return ZoneSystem.instance.m_zoneCtrlPrefab.GetComponent(); } private static SpawnSystemList CreateManagedSpawnList(List spawners) { SpawnSystemList obj = GetManagedSpawnListHost().AddComponent(); ((Object)obj).hideFlags = (HideFlags)61; obj.m_spawners = spawners ?? new List(); obj.m_biomeFolded = new List(); return obj; } private static SpawnSystemList CreateAttachedSpawnList(List spawners) { SpawnSystemList obj = GetAttachedSpawnListHost().AddComponent(); ((Object)obj).hideFlags = (HideFlags)61; obj.m_spawners = spawners ?? new List(); obj.m_biomeFolded = new List(); return obj; } private static GameObject GetManagedSpawnListHost() { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Expected O, but got Unknown if ((Object)(object)_managedSpawnListHost != (Object)null) { return _managedSpawnListHost; } _managedSpawnListHost = new GameObject("DropNSpawn.SpawnSystemLists") { hideFlags = (HideFlags)61 }; Object.DontDestroyOnLoad((Object)(object)_managedSpawnListHost); if ((Object)(object)DropNSpawnPlugin.Instance != (Object)null) { _managedSpawnListHost.transform.SetParent(((Component)DropNSpawnPlugin.Instance).transform, false); } return _managedSpawnListHost; } private static GameObject GetAttachedSpawnListHost() { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Expected O, but got Unknown if ((Object)(object)_attachedSpawnListHost != (Object)null) { return _attachedSpawnListHost; } _attachedSpawnListHost = new GameObject("DropNSpawn.SpawnSystemAttachedLists") { hideFlags = (HideFlags)61 }; Object.DontDestroyOnLoad((Object)(object)_attachedSpawnListHost); if ((Object)(object)DropNSpawnPlugin.Instance != (Object)null) { _attachedSpawnListHost.transform.SetParent(((Component)DropNSpawnPlugin.Instance).transform, false); } return _attachedSpawnListHost; } private static void ClearAttachedRuntimeState(SpawnSystem? system) { if (!((Object)(object)system == (Object)null)) { ClearRuntimeMetadata(system); SpawnSystemCustomDataSupport.ClearCustomData(system); DestroyAttachedSpawnLists(system.m_spawnLists); } } private static void DestroyAttachedSpawnLists(IEnumerable? spawnLists) { if (spawnLists == null) { return; } foreach (SpawnSystemList spawnList in spawnLists) { if (!((Object)(object)spawnList == (Object)null) && (Object)(object)_attachedSpawnListHost != (Object)null && (Object)(object)((Component)spawnList).gameObject == (Object)(object)_attachedSpawnListHost) { Object.Destroy((Object)(object)spawnList); } } } private static void DestroyReplacedCompiledTable(CompiledSpawnSystemTable? table) { if (table == null || table == _activeCompiledTable || table == _vanillaCompiledTable) { return; } foreach (SpawnSystemList list in table.Lists) { if ((Object)(object)list != (Object)null) { Object.Destroy((Object)(object)list); } } table.Lists.Clear(); } internal static void OnSpawnSystemAwake(SpawnSystem? system) { lock (Sync) { TrackLiveSystemLocked(system); if ((Object)(object)system == (Object)null || (Object)(object)ZNetScene.instance == (Object)null || (Object)(object)ObjectDB.instance == (Object)null || DropNSpawnPlugin.IsGameDataRefreshDeferred(DropNSpawnPlugin.ReloadDomain.SpawnSystem)) { return; } bool num = PreAttachedSpawnSystemIds.Remove(((Object)system).GetInstanceID()); CompiledSpawnSystemTable selectedCompiledTableForCurrentState = GetSelectedCompiledTableForCurrentState(); bool flag = num && !IsSystemAttachedToCompiledTable(system, selectedCompiledTableForCurrentState); bool flag2 = !num || flag; if (DropNSpawnPlugin.IsSourceOfTruth) { if (HandleSourceOfTruthSpawnSystemAwake()) { ApplyIfReady(flag2); return; } } else if (!RuntimeState.ConfigurationReady) { if (!CanRetainCurrentCompiledTableWhilePending(ComputeGameDataSignature())) { return; } } else if (RuntimeState.ConfigurationReady && (_activeCompiledTable == null || _activeCompiledTable.Lists.Count == 0)) { ApplyIfReady(flag2, queueLiveSystemAttach: true); if (_activeCompiledTable == null || _activeCompiledTable.Lists.Count == 0) { return; } } AttachCompiledTableToAwakenedSystem(system, flag2); } } internal static void PreAttachCompiledTableToAwakeningSystem(SpawnSystem? system) { lock (Sync) { TrackLiveSystemLocked(system); if (!((Object)(object)system == (Object)null) && ShouldApplyLocally() && !DropNSpawnPlugin.IsGameDataRefreshDeferred(DropNSpawnPlugin.ReloadDomain.SpawnSystem) && (DropNSpawnPlugin.IsSourceOfTruth || ((!RuntimeState.ConfigurationReady || (_activeCompiledTable != null && _activeCompiledTable.Lists.Count != 0)) && (RuntimeState.ConfigurationReady || CanRetainCurrentCompiledTableWhilePending(ComputeGameDataSignature()))))) { CompiledSpawnSystemTable selectedCompiledTableForCurrentState = GetSelectedCompiledTableForCurrentState(); if (selectedCompiledTableForCurrentState != null) { AttachTableToSystem(system, selectedCompiledTableForCurrentState); PreAttachedSpawnSystemIds.Add(((Object)system).GetInstanceID()); } } } } internal static void UntrackLiveSystem(SpawnSystem? system) { lock (Sync) { UntrackLiveSystemLocked(system); } } internal static bool ShouldBlockClientSpawnSystemUpdate(SpawnSystem? system) { lock (Sync) { if (!ShouldApplyLocally() || DropNSpawnPlugin.IsSourceOfTruth) { return false; } if (!RuntimeState.ConfigurationReady) { return true; } if (_activeCompiledTable == null || _activeCompiledTable.Lists.Count == 0) { return true; } return !IsSystemAttachedToCompiledTable(system, _activeCompiledTable); } } private static void QueueEspMarkerRefresh(SpawnSystem? system) { EspSpawnSystemCompatibility.RequestRefresh(system, _reconcileQueueEpoch); } private static void EnsureLiveSystemRegistrySessionLocked() { int num = (((Object)(object)ZNetScene.instance != (Object)null) ? ((Object)ZNetScene.instance).GetInstanceID() : 0); if (_liveSystemsRegistrySceneInstanceId != num) { FinalizeAllPendingCompiledTableRetirementsLocked(); _liveSystemsRegistrySceneInstanceId = num; LiveSystemsById.Clear(); LiveSystemsSnapshot.Clear(); _liveSystemsSnapshotDirty = true; _liveSystemsBootstrapAttempted = false; SnapshotsBySystemId.Clear(); _templateSnapshot = null; PendingLiveSystemAttaches.Clear(); PendingLiveSystemAttachIds.Clear(); PendingLiveSystemAttachEspRefreshIds.Clear(); EspSpawnSystemCompatibility.ClearPendingRefreshes(); PreAttachedSpawnSystemIds.Clear(); ResetPreparedEntriesBuildPipelineLocked(clearPendingTargetSignature: true); } } private static void TrackLiveSystemLocked(SpawnSystem? system) { EnsureLiveSystemRegistrySessionLocked(); if (!((Object)(object)system == (Object)null)) { int instanceID = ((Object)system).GetInstanceID(); LiveSystemsById[instanceID] = system; _liveSystemsSnapshotDirty = true; } } private static void UntrackLiveSystemLocked(SpawnSystem? system) { EnsureLiveSystemRegistrySessionLocked(); if (!((Object)(object)system == (Object)null)) { int instanceID = ((Object)system).GetInstanceID(); if (LiveSystemsById.Remove(instanceID)) { ClearAttachedRuntimeState(system); _liveSystemsSnapshotDirty = true; SnapshotsBySystemId.Remove(instanceID); _templateSnapshot = null; PendingLiveSystemAttachIds.Remove(instanceID); PendingLiveSystemAttachEspRefreshIds.Remove(instanceID); EspSpawnSystemCompatibility.RemovePendingRefresh(instanceID); PreAttachedSpawnSystemIds.Remove(instanceID); MarkSystemMigratedFromRetiredTablesLocked(instanceID); } } } private static bool HandleSourceOfTruthSpawnSystemAwake() { if (_activeCompiledTable != null) { return false; } if (EnsurePrimaryOverrideConfigurationFileExists()) { LoadConfiguration(); } return true; } private static void AttachCompiledTableToAwakenedSystem(SpawnSystem system, bool queueEspRefresh) { CompiledSpawnSystemTable selectedCompiledTableForCurrentState = GetSelectedCompiledTableForCurrentState(); if (selectedCompiledTableForCurrentState != null) { AttachTableToSystem(system, selectedCompiledTableForCurrentState); MarkSystemMigratedFromRetiredTablesLocked(((Object)system).GetInstanceID()); if (queueEspRefresh) { QueueEspMarkerRefresh(system); } } } private static bool TryProcessPendingLiveSystemAttach(double deadline) { while (PendingLiveSystemAttaches.Count > 0) { if (Time.realtimeSinceStartupAsDouble >= deadline) { return false; } if (!PendingLiveSystemAttaches.TryDequeue(out var item)) { continue; } bool flag = PendingLiveSystemAttachEspRefreshIds.Remove(item.SystemId); PendingLiveSystemAttachIds.Remove(item.SystemId); if (item.Epoch == _reconcileQueueEpoch && !((Object)(object)item.System == (Object)null)) { if (item.BuildVersion != BuildPipelineState.PreparedEntriesBuildVersion || item.TargetTable != GetSelectedCompiledTableForCurrentState()) { return true; } if (item.TargetTable == null) { return true; } AttachTableToSystem(item.System, item.TargetTable); MarkSystemMigratedFromRetiredTablesLocked(item.SystemId); if (flag) { QueueEspMarkerRefresh(item.System); } return true; } } return false; } private static bool TryCaptureSnapshotsIfNeeded() { List liveSystems = GetLiveSystems(); if (liveSystems.Count == 0 || (Object)(object)ZNetScene.instance == (Object)null || (Object)(object)ObjectDB.instance == (Object)null) { return false; } PruneSnapshots(liveSystems); foreach (SpawnSystem item in liveSystems) { CaptureSnapshotIfNeeded(item); } return SnapshotsBySystemId.Count > 0; } private static void RefreshSnapshots() { SnapshotsBySystemId.Clear(); _templateSnapshot = null; } private static void InvalidateRuntimeTimeOfDayPhaseMarker() { _lastRuntimeTimeOfDayPhaseMarker = null; _lastRuntimeTimeOfDayRefreshFrame = -1; } private static void PruneSnapshots(IEnumerable systems) { HashSet hashSet = (from system in systems where (Object)(object)system != (Object)null select ((Object)system).GetInstanceID()).ToHashSet(); if (SnapshotsBySystemId.Count == 0) { return; } List list = null; foreach (int key in SnapshotsBySystemId.Keys) { if (!hashSet.Contains(key)) { if (list == null) { list = new List(); } list.Add(key); } } if (list == null) { return; } foreach (int item in list) { SnapshotsBySystemId.Remove(item); } _templateSnapshot = null; } private static bool RefreshTemplateSnapshot() { if (SnapshotsBySystemId.Count > 0) { _templateSnapshot = CaptureTemplateSnapshot(SnapshotsBySystemId.Values); return _templateSnapshot != null; } List liveSystems = GetLiveSystems(); if (liveSystems.Count == 0 || (Object)(object)ZNetScene.instance == (Object)null || (Object)(object)ObjectDB.instance == (Object)null) { return false; } _templateSnapshot = CaptureTemplateSnapshot(liveSystems); return _templateSnapshot != null; } private static SpawnSystemSnapshot CaptureTemplateSnapshot(IEnumerable systems) { return CaptureTemplateSnapshot((from system in systems where (Object)(object)system != (Object)null orderby ((Object)system).GetInstanceID() select system).Select(CaptureSnapshot).ToList()); } private static SpawnSystemSnapshot CaptureTemplateSnapshot(IEnumerable snapshots) { List list = (from snapshot in snapshots where snapshot != null orderby snapshot.SystemId select snapshot).ToList(); if (list.Count == 0) { return new SpawnSystemSnapshot(); } SpawnSystemSnapshot spawnSystemSnapshot = new SpawnSystemSnapshot { SystemId = 0 }; Dictionary dictionary = new Dictionary(StringComparer.Ordinal); foreach (SpawnSystemSnapshot item in list) { Dictionary dictionary2 = new Dictionary(StringComparer.Ordinal); foreach (SpawnSystemEntrySnapshot item2 in item.Entries.OrderBy((SpawnSystemEntrySnapshot result) => result, Comparer.Create(CompareSpawnSystemEntriesForOutput))) { string stableReferenceSortKey = GetStableReferenceSortKey(item2); if (dictionary2.TryGetValue(stableReferenceSortKey, out var value)) { dictionary2[stableReferenceSortKey] = (value.Item1, value.Item2 + 1); } else { dictionary2[stableReferenceSortKey] = (item2, 1); } } foreach (var (key, tuple2) in dictionary2) { if (dictionary.TryGetValue(key, out var value2)) { dictionary[key] = (value2.Item1, Math.Max(value2.Item2, tuple2.Item2)); } else { dictionary[key] = (tuple2.Item1, tuple2.Item2); } } } List list2 = dictionary.Values.OrderBy<(SpawnSystemEntrySnapshot, int), SpawnSystemEntrySnapshot>(((SpawnSystemEntrySnapshot Representative, int MaxCount) entry) => entry.Representative, Comparer.Create(CompareSpawnSystemEntriesForOutput)).SelectMany<(SpawnSystemEntrySnapshot, int), SpawnSystemEntrySnapshot>(((SpawnSystemEntrySnapshot Representative, int MaxCount) entry) => from _ in Enumerable.Range(0, entry.MaxCount) select entry.Representative).ToList(); int num = 0; foreach (SpawnSystemEntrySnapshot item3 in list2) { spawnSystemSnapshot.Entries.Add(new SpawnSystemEntrySnapshot { ListIndex = 0, EntryIndex = num++, Data = item3.Data.Clone() }); } return spawnSystemSnapshot; } private static SpawnSystemSnapshot CaptureSnapshotIfNeeded(SpawnSystem system) { int instanceID = ((Object)system).GetInstanceID(); if (SnapshotsBySystemId.TryGetValue(instanceID, out SpawnSystemSnapshot value)) { return value; } value = CaptureSnapshot(system); SnapshotsBySystemId[instanceID] = value; _templateSnapshot = null; return value; } private static SpawnSystemSnapshot CaptureSnapshot(SpawnSystem system) { SpawnSystemSnapshot spawnSystemSnapshot = new SpawnSystemSnapshot { SystemId = ((Object)system).GetInstanceID() }; for (int i = 0; i < system.m_spawnLists.Count; i++) { SpawnSystemList val = system.m_spawnLists[i]; for (int j = 0; j < val.m_spawners.Count; j++) { SpawnData val2 = val.m_spawners[j]; SpawnSystemEntrySnapshot item = new SpawnSystemEntrySnapshot { ListIndex = i, EntryIndex = j, Data = val2.Clone() }; spawnSystemSnapshot.Entries.Add(item); } } return spawnSystemSnapshot; } private static List GetLiveSystems() { EnsureLiveSystemRegistrySessionLocked(); EnsureLiveSystemsBootstrappedLocked(); PruneTrackedLiveSystemsLocked(); if (!_liveSystemsSnapshotDirty) { return LiveSystemsSnapshot; } LiveSystemsSnapshot.Clear(); foreach (SpawnSystem value in LiveSystemsById.Values) { if ((Object)(object)value != (Object)null) { LiveSystemsSnapshot.Add(value); } } LiveSystemsSnapshot.Sort((SpawnSystem left, SpawnSystem right) => ((Object)left).GetInstanceID().CompareTo(((Object)right).GetInstanceID())); _liveSystemsSnapshotDirty = false; return LiveSystemsSnapshot; } private static void EnsureLiveSystemsBootstrappedLocked() { if (_liveSystemsBootstrapAttempted || LiveSystemsById.Count > 0) { return; } _liveSystemsBootstrapAttempted = true; if (SpawnSystemInstancesField?.GetValue(null) is List list) { foreach (SpawnSystem item in list) { TrackLiveSystemLocked(item); } if (LiveSystemsById.Count > 0) { return; } } SpawnSystem[] array = Object.FindObjectsByType((FindObjectsSortMode)0); for (int i = 0; i < array.Length; i++) { TrackLiveSystemLocked(array[i]); } } private static void PruneTrackedLiveSystemsLocked() { if (LiveSystemsById.Count == 0) { return; } List list = null; foreach (var (item, val2) in LiveSystemsById) { if (!((Object)(object)val2 != (Object)null)) { if (list == null) { list = new List(); } list.Add(item); } } if (list == null) { return; } foreach (int item2 in list) { LiveSystemsById.Remove(item2); SnapshotsBySystemId.Remove(item2); PendingLiveSystemAttachIds.Remove(item2); PendingLiveSystemAttachEspRefreshIds.Remove(item2); EspSpawnSystemCompatibility.RemovePendingRefresh(item2); PreAttachedSpawnSystemIds.Remove(item2); MarkSystemMigratedFromRetiredTablesLocked(item2); } _liveSystemsSnapshotDirty = true; _templateSnapshot = null; } private static SpawnSystemSnapshot? GetTemplateSnapshot() { if (_templateSnapshot == null) { RefreshTemplateSnapshot(); } return _templateSnapshot; } private static string ComputePreparedEntriesSignature(List entries) { return NetworkPayloadSyncSupport.ComputeSpawnSystemProjectedConfigurationSignature(entries, (PreparedSpawnSystemEntry entry) => entry.Entry); } private static bool ShouldSkipLowTierBiomeGlobalKeySpawnSystemEntry(SpawnData data) { //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_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Invalid comparison between Unknown and I4 if (!PluginSettingsFacade.ShouldDisableLowTierBiomeGlobalKeySpawnSystemEntries()) { return false; } if (data == null || string.IsNullOrWhiteSpace(data.m_requiredGlobalKey)) { return false; } return (data.m_biome & LowTierBiomeGlobalKeySpawnSystemFilter) > 0; } private static ReferenceCatalogSnapshot BuildCurrentReferenceCatalogSnapshot() { return BuildReferenceCatalogSnapshot(); } private static string BuildReferenceConfigurationTemplate(ReferenceCatalogSnapshot? referenceCatalogSnapshot = null) { if (referenceCatalogSnapshot == null) { referenceCatalogSnapshot = BuildReferenceCatalogSnapshot(); } return SerializeReferenceEntries(referenceCatalogSnapshot.LiveEntries); } private static string SerializeReferenceEntries(IEnumerable entries) { StringBuilder stringBuilder = new StringBuilder(); bool flag = false; foreach (PrefabOwnerSection item in BuildBiomeOrderedReferenceSections(entries ?? Enumerable.Empty())) { if (item.Entries.Count == 0) { continue; } if (flag) { stringBuilder.AppendLine(); } PrefabOutputSections.AppendSectionHeaderComment(stringBuilder, item.OwnerName); foreach (CanonicalSpawnSystemEntry entry in item.Entries) { AppendReferenceEntry(stringBuilder, entry); flag = true; } } if (!flag) { return "[]" + Environment.NewLine; } return stringBuilder.ToString(); } private static void WriteReferenceConfigurationFile(string content, string logMessage) { GeneratedArtifactWriter.WriteText(ReferenceConfigurationPath, content, logMessage, logOnlyWhenChanged: true); } private static void EnsureReferenceArtifactsUpToDate() { if (DropNSpawnPlugin.IsSourceOfTruth && !((Object)(object)ZNetScene.instance == (Object)null) && !((Object)(object)ObjectDB.instance == (Object)null)) { ReferenceCatalogSnapshot referenceCatalogSnapshot = BuildCurrentReferenceCatalogSnapshot(); if (referenceCatalogSnapshot.HasAnyEntries && ReferenceArtifactLifecycle.TryPlanUpdate("spawnsystem", ReferenceConfigurationPath, referenceCatalogSnapshot.SourceSignature, out var updateKind)) { WriteReferenceConfigurationFile(BuildReferenceConfigurationTemplate(referenceCatalogSnapshot), ReferenceArtifactLifecycle.FormatAction(updateKind) + " spawnsystem reference configuration at " + ReferenceConfigurationPath + "."); ReferenceArtifactLifecycle.RecordUpdate("spawnsystem", ReferenceConfigurationPath, referenceCatalogSnapshot.SourceSignature); } } } internal static bool TryWriteFullScaffoldConfigurationFile(out string path, out string error) { string content; string logMessage; lock (Sync) { path = FullScaffoldConfigurationPath; error = ""; if (!TryCaptureSnapshotsIfNeeded()) { error = "SpawnSystem game data is not ready yet."; return false; } content = BuildFullScaffoldConfigurationTemplate(); logMessage = "Wrote spawnsystem full scaffold configuration to " + path + "."; } GeneratedArtifactWriter.WriteTextAlways(path, content, logMessage); return true; } internal static bool TryWriteReferenceConfigurationFile(out string path, out string error) { string content; string sourceSignature; string logMessage; lock (Sync) { path = ReferenceConfigurationPath; error = ""; if ((Object)(object)ZNetScene.instance == (Object)null || (Object)(object)ObjectDB.instance == (Object)null) { error = "SpawnSystem game data is not ready yet."; return false; } ReferenceCatalogSnapshot referenceCatalogSnapshot = BuildCurrentReferenceCatalogSnapshot(); if (!referenceCatalogSnapshot.HasAnyEntries) { error = "SpawnSystem game data is not ready yet."; return false; } content = BuildReferenceConfigurationTemplate(referenceCatalogSnapshot); sourceSignature = referenceCatalogSnapshot.SourceSignature; logMessage = "Updated spawnsystem reference configuration at " + ReferenceConfigurationPath + "."; path = ReferenceConfigurationPath; } WriteReferenceConfigurationFile(content, logMessage); ReferenceArtifactLifecycle.RecordUpdate("spawnsystem", ReferenceConfigurationPath, sourceSignature); return true; } internal static void RefreshReferenceConfigurationFile() { string content; string sourceSignature; string logMessage; lock (Sync) { if ((Object)(object)ZNetScene.instance == (Object)null || (Object)(object)ObjectDB.instance == (Object)null) { return; } ReferenceCatalogSnapshot referenceCatalogSnapshot = BuildCurrentReferenceCatalogSnapshot(); if (!referenceCatalogSnapshot.HasAnyEntries) { return; } content = BuildReferenceConfigurationTemplate(referenceCatalogSnapshot); sourceSignature = referenceCatalogSnapshot.SourceSignature; logMessage = "Updated spawnsystem reference configuration at " + ReferenceConfigurationPath + "."; } WriteReferenceConfigurationFile(content, logMessage); ReferenceArtifactLifecycle.RecordUpdate("spawnsystem", ReferenceConfigurationPath, sourceSignature); } private static CreatureManagerSpawnReferenceSupport.ReferenceSnapshot? TryGetExternalReferenceProjectionSnapshot(bool forceRefresh) { try { return CreatureManagerSpawnReferenceSupport.GetReferenceSnapshot(forceRefresh); } catch (Exception arg) { DropNSpawnPlugin.DropNSpawnLogger.LogWarning((object)$"Failed to collect external spawnsystem reference projections. {arg}"); return null; } } private static List GetExternalReferenceProjectionEntries(bool forceRefresh) { return (from projection in TryGetExternalReferenceProjectionSnapshot(forceRefresh)?.Projections select projection.Entry into entry where entry != null && !string.IsNullOrWhiteSpace(entry.Prefab) select entry).ToList() ?? new List(); } private static List MergeUniqueReferenceEntriesWithExternalProjections(IEnumerable entries, bool forceRefresh) { List list = entries?.ToList() ?? new List(); HashSet hashSet = new HashSet(list.Select(GetStableReferenceSortKey), StringComparer.Ordinal); foreach (CanonicalSpawnSystemEntry externalReferenceProjectionEntry in GetExternalReferenceProjectionEntries(forceRefresh)) { if (hashSet.Add(GetStableReferenceSortKey(externalReferenceProjectionEntry))) { list.Add(externalReferenceProjectionEntry); } } return list; } private static List MergeScaffoldEntriesWithExternalProjections(IEnumerable entries, bool forceRefresh) { List list = entries?.ToList() ?? new List(); Dictionary dictionary = new Dictionary(StringComparer.Ordinal); foreach (CanonicalSpawnSystemEntry item in list) { string stableReferenceSortKey = GetStableReferenceSortKey(item); dictionary[stableReferenceSortKey] = ((!dictionary.TryGetValue(stableReferenceSortKey, out var value)) ? 1 : (value + 1)); } foreach (CanonicalSpawnSystemEntry externalReferenceProjectionEntry in GetExternalReferenceProjectionEntries(forceRefresh)) { string stableReferenceSortKey2 = GetStableReferenceSortKey(externalReferenceProjectionEntry); if (dictionary.TryGetValue(stableReferenceSortKey2, out var value2) && value2 > 0) { dictionary[stableReferenceSortKey2] = value2 - 1; } else { list.Add(externalReferenceProjectionEntry); } } return list; } private static List BuildTemplateReferenceEntries(SpawnSystemSnapshot snapshot) { List list = new List(); foreach (PrefabOwnerSection item in BuildBiomeOrderedSnapshotSections(snapshot)) { foreach (SpawnSystemEntrySnapshot entry in item.Entries) { CanonicalSpawnSystemEntry canonicalSpawnSystemEntry = ConvertToReferenceEntry(entry); canonicalSpawnSystemEntry.ReferenceOwnerName = item.OwnerName; list.Add(canonicalSpawnSystemEntry); } } return list; } private static List BuildTemplateFullScaffoldEntries(SpawnSystemSnapshot snapshot) { List list = new List(); foreach (PrefabOwnerSection item in BuildBiomeOrderedSnapshotSections(snapshot)) { foreach (SpawnSystemEntrySnapshot entry in item.Entries) { CanonicalSpawnSystemEntry canonicalSpawnSystemEntry = ConvertToConfigurationEntry(entry); canonicalSpawnSystemEntry.ReferenceOwnerName = item.OwnerName; list.Add(canonicalSpawnSystemEntry); } } return list; } private static ReferenceCatalogSnapshot BuildReferenceCatalogSnapshot() { ReferenceCatalogSnapshot referenceCatalogSnapshot = new ReferenceCatalogSnapshot(); HashSet hashSet = new HashSet(StringComparer.Ordinal); foreach (SpawnData item in EnumerateUpstreamReferenceSpawnData()) { CanonicalSpawnSystemEntry canonicalSpawnSystemEntry = ConvertToReferenceEntry(item); string stableReferenceSortKey = GetStableReferenceSortKey(canonicalSpawnSystemEntry); if (hashSet.Add(stableReferenceSortKey)) { referenceCatalogSnapshot.LiveEntries.Add(canonicalSpawnSystemEntry); } } List collection = MergeUniqueReferenceEntriesWithExternalProjections(referenceCatalogSnapshot.LiveEntries, forceRefresh: true); referenceCatalogSnapshot.LiveEntries.Clear(); referenceCatalogSnapshot.LiveEntries.AddRange(collection); referenceCatalogSnapshot.LiveEntries.Sort(CompareReferenceEntriesForOutput); string value = SerializeReferenceEntries(referenceCatalogSnapshot.LiveEntries); referenceCatalogSnapshot.SourceSignature = ReferenceRefreshSupport.ComputeStableHash(value); return referenceCatalogSnapshot; } private static IEnumerable EnumerateUpstreamReferenceSpawnData() { if (TryEnumerateReferenceSpawnData(GetReferenceSourceSpawnLists(), out IEnumerable spawnData)) { return spawnData; } return Enumerable.Empty(); } private static IEnumerable GetReferenceSourceSpawnLists() { SpawnSystem zoneCtrlPrefabSpawnSystem = GetZoneCtrlPrefabSpawnSystem(); if (zoneCtrlPrefabSpawnSystem?.m_spawnLists != null) { return zoneCtrlPrefabSpawnSystem.m_spawnLists.Where((SpawnSystemList spawnList) => (Object)(object)spawnList != (Object)null); } if (_vanillaCompiledTable != null && _vanillaCompiledTable.ReferenceSourceTrusted && _vanillaCompiledTable.Lists.Count > 0) { return _vanillaCompiledTable.Lists.Where((SpawnSystemList spawnList) => (Object)(object)spawnList != (Object)null); } return Enumerable.Empty(); } private static IEnumerable GetSpawnSystemShorthandFieldExampleLines() { yield return "- prefab: Boar"; yield return " spawnSystem: # Flat SpawnSystem row # Controls spawn timing, placement, limits, native conditions, and custom data"; yield return " name: null # ex) Boar # Optional row name # Omit to default to the prefab name in this compressed format"; yield return " huntPlayer: false # True marks the spawned AI as hunting the player"; yield return " level: 1~1 # ex) 1~3 # Range of spawned creature levels"; yield return " overrideLevelUpChance: -1 # Use -1 to keep native behavior # Percent per extra level roll"; yield return " levelUpMinCenterDistance: 0 # Meters from the world center before native level-up rolls start"; yield return " groundOffset: 0.5 # Meters of vertical placement offset"; yield return " groundOffsetRandom: 0 # Range in meters of random vertical placement offset"; yield return " spawnInterval: 4 # ex) 100 # Seconds between spawn checks"; yield return " spawnChance: 100 # Percent chance per successful check"; yield return " spawnRadius: 40~80 # ex) 20~60 # Range in meters from one player to the chosen spawn center # Native effective default; 0 on min/max uses the native 40/80 fallback"; yield return " groupSize: 1~1 # ex) 1~3 # Range of creatures spawned by one successful attempt"; yield return " groupRadius: 3 # Meters from the chosen spawn center to each spawned creature"; yield return " noSpawnRadius: 10 # Meters from the chosen spawn center used to block the attempt if the same prefab is already nearby"; yield return " maxSpawned: 1 # Active prefab count cap in the wider area loaded around players"; yield return " tilt: 0~35 # Range in degrees of allowed ground tilt"; yield return " altitude: -1000~1000 # Range in world-height meters"; yield return " oceanDepth: 0~0 # ex) 0~10 # Range in meters of water depth at the spawn point # 0~0 leaves the native depth check effectively unconstrained"; yield return " distanceFromCenter: 0~0 # ex) 0~10000 # Range in meters from the world center # 0~0 leaves the native distance check effectively unconstrained"; yield return " biomes: [Meadows] # Allowed spawn biomes # Expand World Data custom biome names and numeric biome masks also work when EWD is installed"; yield return " biomeAreas: [Everything] # Allowed values: Edge, Median, Everything # Edge = biome border band # Median = biome interior # Everything = both"; yield return " timeOfDay: null # ex) [day, night]"; yield return " requiredEnvironments: [] # ex) [Rain, Clear] # Allowed environment names"; yield return " requiredGlobalKey: '' # ex) defeated_gdking # Native default is '' # Supports 'key 10' numeric syntax too"; yield return " inLava: false # True = lava only # False = outside lava only"; yield return " inForest: null # ex) true = forest only # false = outside forest only # null or no field allows both"; yield return " insidePlayerBase: false # False = outside player-base influence only"; yield return " canSpawnCloseToPlayer: false # True allows close-to-player spawns"; yield return " fields: {} # ex) { Character.m_name: $enemy_boar, health: 200, damage: 2 } # Expand World Data field overrides"; yield return " objects: [] # ex) [Wood,0,0,0,1] # Expand World Data object entries"; yield return " data: null # Expand World Data data entry name from your EWD config"; yield return " faction: null # ex) ForestMonsters # Values: " + FactionIntegration.GetNativeFactionList(); } private static string BuildFullScaffoldConfigurationTemplate() { SpawnSystemSnapshot templateSnapshot = GetTemplateSnapshot(); if (templateSnapshot == null) { return ""; } StringBuilder stringBuilder = new StringBuilder(); bool flag = false; foreach (PrefabOwnerSection item in BuildBiomeOrderedReferenceSections(MergeScaffoldEntriesWithExternalProjections(BuildTemplateFullScaffoldEntries(templateSnapshot), forceRefresh: true))) { foreach (CanonicalSpawnSystemEntry entry in item.Entries) { if (flag) { stringBuilder.AppendLine(); } AppendConfigurationEntry(stringBuilder, entry); flag = true; } } if (!flag) { return "[]" + Environment.NewLine; } return stringBuilder.ToString(); } private static string BuildCompressedPrimaryOverrideConfigurationDocument(SpawnSystemSnapshot snapshot) { StringBuilder stringBuilder = new StringBuilder(); CommentedYamlTemplateSupport.AppendTemplateComment(stringBuilder, "This file is auto-loaded from " + Path.GetFileName(PrimaryOverrideConfigurationPathYml) + " or " + Path.GetFileName(PrimaryOverrideConfigurationPathYaml) + ", plus any " + PluginSettingsFacade.GetYamlDomainSupplementalPrefix("spawnsystem") + "*.yml/.yaml files."); CommentedYamlTemplateSupport.AppendTemplateComment(stringBuilder, "Loaded files are concatenated in filename order after the primary file."); CommentedYamlTemplateSupport.AppendTemplateComment(stringBuilder, "This auto-created override is seeded from the current live SpawnSystem snapshot only and does not merge external CreatureManager/Jotunn reference projections."); CommentedYamlTemplateSupport.AppendTemplateComment(stringBuilder, "Use dns:reference spawnsystem when you want to export " + PluginSettingsFacade.GetYamlDomainFilePrefix("spawnsystem") + ".reference.yml for a compact reference snapshot, or " + PluginSettingsFacade.GetYamlDomainFilePrefix("spawnsystem") + ".full.yml for exhaustive field examples."); CommentedYamlTemplateSupport.AppendTemplateComment(stringBuilder, "Applying this file still strictly replaces the live SpawnSystem table with the rows defined here."); CommentedYamlTemplateSupport.AppendTemplateComment(stringBuilder, "requiredGlobalKey also supports 'key 10' to require at least that numeric value and consume it after each successful spawn."); foreach (string spawnSystemShorthandFieldExampleLine in GetSpawnSystemShorthandFieldExampleLines()) { CommentedYamlTemplateSupport.AppendTemplateComment(stringBuilder, spawnSystemShorthandFieldExampleLine); } CommentedYamlTemplateSupport.AppendTemplateBlankLine(stringBuilder); CommentedYamlTemplateSupport.AppendTemplateComment(stringBuilder, "Optional file header: '- spawnIntervalMultiplier: 2.0' must be the first list item and scales only this file's SpawnSystem intervals."); CommentedYamlTemplateSupport.AppendTemplateComment(stringBuilder, "The multiplier must be greater than zero. 0.5 halves intervals (more frequent checks); 2.0 doubles intervals (less frequent checks)."); CommentedYamlTemplateSupport.AppendTemplateComment(stringBuilder, "- spawnIntervalMultiplier: 1.0"); stringBuilder.AppendLine(); bool flag = false; foreach (PrefabOwnerSection item in BuildBiomeOrderedReferenceSections(BuildTemplateReferenceEntries(snapshot))) { if (item.Entries.Count == 0) { continue; } if (flag) { stringBuilder.AppendLine(); } foreach (CanonicalSpawnSystemEntry entry in item.Entries) { AppendReferenceEntry(stringBuilder, entry); stringBuilder.AppendLine(); } flag = true; } if (!flag) { return "[]" + Environment.NewLine; } return stringBuilder.ToString(); } } internal static class SceneTraversalSupport { internal static void VisitActiveLoadedSceneGameObjects(Action visitor) { //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) if (visitor == null) { return; } List pendingTransforms = new List(); int sceneCount = SceneManager.sceneCount; for (int i = 0; i < sceneCount; i++) { Scene sceneAt = SceneManager.GetSceneAt(i); if (!((Scene)(ref sceneAt)).isLoaded) { continue; } GameObject[] rootGameObjects = ((Scene)(ref sceneAt)).GetRootGameObjects(); foreach (GameObject val in rootGameObjects) { if ((Object)(object)val == (Object)null || !val.activeInHierarchy) { continue; } TraverseHierarchy(val.transform, delegate(Transform transform) { GameObject gameObject = ((Component)transform).gameObject; if ((Object)(object)gameObject != (Object)null && gameObject.activeInHierarchy) { visitor(gameObject); } }, pendingTransforms); } } } private static void TraverseHierarchy(Transform? root, Action visitor, List pendingTransforms) { if ((Object)(object)root == (Object)null || visitor == null) { return; } pendingTransforms.Clear(); pendingTransforms.Add(root); while (pendingTransforms.Count > 0) { int index = pendingTransforms.Count - 1; Transform val = pendingTransforms[index]; pendingTransforms.RemoveAt(index); if ((Object)(object)val == (Object)null) { continue; } visitor(val); for (int i = 0; i < val.childCount; i++) { Transform child = val.GetChild(i); if ((Object)(object)child != (Object)null) { pendingTransforms.Add(child); } } } } } internal sealed class SpawnerConfigurationEntry { [YamlMember(Order = 1)] public string Prefab { get; set; } = ""; [YamlMember(Order = 2)] public bool Enabled { get; set; } = true; [YamlMember(Order = 3)] public List? Locations { get; set; } [YamlMember(Order = 4)] public ConditionsDefinition? Conditions { get; set; } [YamlMember(Order = 5)] public SpawnAreaDefinition? SpawnArea { get; set; } [YamlMember(Order = 6)] public CreatureSpawnerDefinition? CreatureSpawner { get; set; } [YamlIgnore] public string RuleId { get; set; } = ""; [YamlIgnore] public string? SourcePath { get; set; } [YamlIgnore] public int SourceLine { get; set; } [YamlIgnore] public int SourceColumn { get; set; } } internal sealed class SpawnerReferenceEntry { [YamlMember(Order = 1)] public string Prefab { get; set; } = ""; [YamlMember(Order = 2)] public SpawnAreaDefinition? SpawnArea { get; set; } [YamlMember(Order = 3)] public CreatureSpawnerDefinition? CreatureSpawner { get; set; } } internal sealed class SpawnerLocationReferenceEntry { [YamlMember(Order = 1)] public string Prefab { get; set; } = ""; [YamlMember(Order = 2)] public List Locations { get; set; } = new List(); } internal sealed class SpawnAreaDefinition { [YamlMember(Order = 1)] public float? LevelUpChance { get; set; } [YamlMember(Order = 2)] public float? SpawnInterval { get; set; } [YamlMember(Order = 3)] public float? TriggerDistance { get; set; } [YamlMember(Order = 4)] public bool? SetPatrolSpawnPoint { get; set; } [YamlMember(Order = 5)] public float? SpawnRadius { get; set; } [YamlMember(Order = 6)] public float? NearRadius { get; set; } [YamlMember(Order = 7)] public float? FarRadius { get; set; } [YamlMember(Order = 8)] public int? MaxNear { get; set; } [YamlMember(Order = 9)] public int? MaxTotal { get; set; } [YamlMember(Order = 10)] public int? MaxTotalSpawns { get; set; } [YamlMember(Order = 11)] public bool? OnGroundOnly { get; set; } [YamlMember(Order = 12)] public List? Creatures { get; set; } } internal sealed class SpawnAreaSpawnDefinition { [YamlMember(Order = 1)] public string Creature { get; set; } = ""; [YamlMember(Order = 2)] public float? Weight { get; set; } [YamlMember(Order = 3)] public IntRangeDefinition? Level { get; set; } [YamlIgnore] public int? MinLevel { get; set; } [YamlIgnore] public int? MaxLevel { get; set; } [YamlMember(Order = 4)] public string? Faction { get; set; } [YamlMember(Order = 5)] public string? Data { get; set; } [YamlMember(Order = 6)] public Dictionary? Fields { get; set; } [YamlMember(Order = 7)] public List? Objects { get; set; } } internal sealed class CreatureSpawnerDefinition { [YamlMember(Order = 1)] public string? Creature { get; set; } [YamlMember(Order = 2)] public TimeOfDayDefinition? TimeOfDay { get; set; } [YamlMember(Order = 3)] public string? RequiredGlobalKey { get; set; } [YamlMember(Order = 4)] public string? BlockingGlobalKey { get; set; } [YamlMember(Order = 5)] public IntRangeDefinition? Level { get; set; } [YamlIgnore] public int? MinLevel { get; set; } [YamlIgnore] public int? MaxLevel { get; set; } [YamlMember(Order = 6)] public float? LevelUpChance { get; set; } [YamlMember(Order = 7)] public float? RespawnTimeMinutes { get; set; } [YamlMember(Order = 8)] public int? SpawnCheckInterval { get; set; } [YamlMember(Order = 9)] public int? SpawnGroupId { get; set; } [YamlMember(Order = 10)] public float? SpawnGroupRadius { get; set; } [YamlMember(Order = 11)] public float? SpawnerWeight { get; set; } [YamlMember(Order = 12)] public int? MaxGroupSpawned { get; set; } [YamlMember(Order = 13)] public float? TriggerDistance { get; set; } [YamlMember(Order = 14)] public float? TriggerNoise { get; set; } [YamlMember(Order = 15)] public bool? RequireSpawnArea { get; set; } [YamlMember(Order = 16)] public bool? AllowInsidePlayerBase { get; set; } [YamlMember(Order = 17)] public bool? WakeUpAnimation { get; set; } [YamlMember(Order = 18)] public bool? SetPatrolSpawnPoint { get; set; } [YamlMember(Order = 19)] public string? Faction { get; set; } [YamlMember(Order = 20)] public string? Data { get; set; } [YamlMember(Order = 21)] public Dictionary? Fields { get; set; } [YamlMember(Order = 22)] public List? Objects { get; set; } } internal static class SpawnerManager { private readonly struct PendingSpawnAreaReconcile { public SpawnArea SpawnArea { get; } public int InstanceId { get; } public int Epoch { get; } public PendingSpawnAreaReconcile(SpawnArea spawnArea, int instanceId, int epoch) { SpawnArea = spawnArea; InstanceId = instanceId; Epoch = epoch; } } private readonly struct PendingCreatureSpawnerReconcile { public CreatureSpawner CreatureSpawner { get; } public int InstanceId { get; } public int Epoch { get; } public PendingCreatureSpawnerReconcile(CreatureSpawner creatureSpawner, int instanceId, int epoch) { CreatureSpawner = creatureSpawner; InstanceId = instanceId; Epoch = epoch; } } private sealed class MatchingEntryCache { private readonly List _entries = new List(); private readonly List _runtimeEntries = new List(); private readonly List _runtimeRequiredGlobalKeys = new List(); private readonly List _runtimeForbiddenGlobalKeys = new List(); public string ConfigPrefabName { get; set; } = ""; public string ResolvedLocationKey { get; set; } = ""; public bool UsesLocationSelector { get; set; } public bool HasRecordedLocationProvenanceEpoch { get; set; } public int RecordedLocationProvenanceEpoch { get; set; } public SharedMatchingEntryTemplate? SharedTemplate { get; private set; } public IReadOnlyList Entries => SharedTemplate?.Entries ?? _entries; public IReadOnlyList RuntimeEntries => SharedTemplate?.RuntimeEntries ?? _runtimeEntries; public Dictionary WinningEntriesByRuntimeSignature { get; } = new Dictionary(); public IReadOnlyList RuntimeRequiredGlobalKeys => SharedTemplate?.RuntimeRequiredGlobalKeys ?? _runtimeRequiredGlobalKeys; public IReadOnlyList RuntimeForbiddenGlobalKeys => SharedTemplate?.RuntimeForbiddenGlobalKeys ?? _runtimeForbiddenGlobalKeys; public bool UsesTimeOfDay { get; set; } public bool UsesRequiredEnvironments { get; set; } public bool UsesInsidePlayerBase { get; set; } internal List MutableEntries => _entries; internal List MutableRuntimeEntries => _runtimeEntries; internal List MutableRuntimeRequiredGlobalKeys => _runtimeRequiredGlobalKeys; internal List MutableRuntimeForbiddenGlobalKeys => _runtimeForbiddenGlobalKeys; public void UseSharedTemplate(SharedMatchingEntryTemplate template) { SharedTemplate = template; ConfigPrefabName = template.ConfigPrefabName; ResolvedLocationKey = template.ResolvedLocationKey; UsesLocationSelector = template.UsesLocationSelector; UsesTimeOfDay = template.UsesTimeOfDay; UsesRequiredEnvironments = template.UsesRequiredEnvironments; UsesInsidePlayerBase = template.UsesInsidePlayerBase; _entries.Clear(); _runtimeEntries.Clear(); _runtimeRequiredGlobalKeys.Clear(); _runtimeForbiddenGlobalKeys.Clear(); } } private sealed class SharedMatchingEntryTemplate { public string ConfigPrefabName { get; set; } = ""; public string ResolvedLocationKey { get; set; } = ""; public bool UsesLocationSelector { get; set; } public List Entries { get; } = new List(); public List RuntimeEntries { get; } = new List(); public List RuntimeRequiredGlobalKeys { get; } = new List(); public List RuntimeForbiddenGlobalKeys { get; } = new List(); public bool UsesTimeOfDay { get; set; } public bool UsesRequiredEnvironments { get; set; } public bool UsesInsidePlayerBase { get; set; } } private sealed class RuntimeContextSnapshot { public int Frame { get; set; } public int TimeOfDayPhaseMarker { get; set; } public string EnvironmentName { get; set; } = ""; public Dictionary GlobalKeyStates { get; } = new Dictionary(StringComparer.OrdinalIgnoreCase); } private sealed class LocalRuntimeState { public float LastInsidePlayerBaseSampleTime { get; set; } = float.NegativeInfinity; public float NextRuntimeEvaluationTime { get; set; } = float.NegativeInfinity; public bool IsInsidePlayerBase { get; set; } public int LastObservedTimeOfDayPhaseMarker { get; set; } = int.MinValue; public string LastObservedEnvironmentName { get; set; } = ""; public bool HasAppliedWinningEntrySelection { get; set; } public string LastAppliedConfigPrefabName { get; set; } = ""; public string LastAppliedResolvedLocationKey { get; set; } = ""; public string LastAppliedWinningEntryRuleId { get; set; } = ""; } private sealed class SpawnAreaResolvedSpawnEntry { public SpawnData SpawnData { get; set; } public SpawnAreaSpawnDefinition Definition { get; set; } public ExpandWorldSpawnDataPayload? DataPayload { get; set; } } private sealed class SpawnAreaSpawnSnapshot { public GameObject? Prefab { get; set; } public float Weight { get; set; } public int MinLevel { get; set; } public int MaxLevel { get; set; } } private sealed class SpawnAreaComponentSnapshot { public SpawnArea Component { get; set; } public string ConfigPrefabName { get; set; } = ""; public string RootPrefabName { get; set; } = ""; public string RelativePath { get; set; } = ""; public float LevelUpChance { get; set; } public float SpawnInterval { get; set; } public float TriggerDistance { get; set; } public bool SetPatrolSpawnPoint { get; set; } public float SpawnRadius { get; set; } public float NearRadius { get; set; } public float FarRadius { get; set; } public int MaxNear { get; set; } public int MaxTotal { get; set; } public bool OnGroundOnly { get; set; } public List Prefabs { get; set; } = new List(); } private sealed class SpawnAreaLiveSnapshot { public float LevelUpChance { get; set; } public float SpawnInterval { get; set; } public float TriggerDistance { get; set; } public bool SetPatrolSpawnPoint { get; set; } public float SpawnRadius { get; set; } public float NearRadius { get; set; } public float FarRadius { get; set; } public int MaxNear { get; set; } public int MaxTotal { get; set; } public bool OnGroundOnly { get; set; } public List Prefabs { get; set; } = new List(); } private sealed class CreatureSpawnerComponentSnapshot { public CreatureSpawner Component { get; set; } public string ConfigPrefabName { get; set; } = ""; public string RootPrefabName { get; set; } = ""; public string RelativePath { get; set; } = ""; public GameObject? CreaturePrefab { get; set; } public int MinLevel { get; set; } public int MaxLevel { get; set; } public float LevelUpChance { get; set; } public float RespawnTimeMinutes { get; set; } public float TriggerDistance { get; set; } public float TriggerNoise { get; set; } public bool SpawnAtNight { get; set; } public bool SpawnAtDay { get; set; } public bool RequireSpawnArea { get; set; } public bool SpawnInPlayerBase { get; set; } public bool WakeUpAnimation { get; set; } public int SpawnCheckInterval { get; set; } public string RequiredGlobalKey { get; set; } = ""; public string BlockingGlobalKey { get; set; } = ""; public bool SetPatrolSpawnPoint { get; set; } public int SpawnGroupId { get; set; } public int MaxGroupSpawned { get; set; } public float SpawnGroupRadius { get; set; } public float SpawnerWeight { get; set; } } private sealed class CreatureSpawnerLiveSnapshot { public GameObject? CreaturePrefab { get; set; } public int MinLevel { get; set; } public int MaxLevel { get; set; } public float LevelUpChance { get; set; } public float RespawnTimeMinutes { get; set; } public float TriggerDistance { get; set; } public float TriggerNoise { get; set; } public bool SpawnAtNight { get; set; } public bool SpawnAtDay { get; set; } public bool RequireSpawnArea { get; set; } public bool SpawnInPlayerBase { get; set; } public bool WakeUpAnimation { get; set; } public int SpawnCheckInterval { get; set; } public string RequiredGlobalKey { get; set; } = ""; public string BlockingGlobalKey { get; set; } = ""; public bool SetPatrolSpawnPoint { get; set; } public int SpawnGroupId { get; set; } public int MaxGroupSpawned { get; set; } public float SpawnGroupRadius { get; set; } public float SpawnerWeight { get; set; } } private sealed class SyncedSpawnerConfigurationState { public List Configuration { get; set; } = new List(); public List ActiveEntries { get; } = new List(); public Dictionary> ActiveEntriesByPrefab { get; } = new Dictionary>(StringComparer.OrdinalIgnoreCase); public HashSet ConfiguredSpawnAreaPrefabs { get; } = new HashSet(StringComparer.OrdinalIgnoreCase); public HashSet ConfiguredCreatureSpawnerPrefabs { get; } = new HashSet(StringComparer.OrdinalIgnoreCase); public HashSet RuntimeConfiguredSpawnAreaPrefabs { get; } = new HashSet(StringComparer.OrdinalIgnoreCase); public HashSet RuntimeConfiguredCreatureSpawnerPrefabs { get; } = new HashSet(StringComparer.OrdinalIgnoreCase); public Dictionary EntrySignaturesByPrefab { get; set; } = new Dictionary(StringComparer.OrdinalIgnoreCase); public string ConfigurationSignature { get; set; } = ""; } private sealed class SpawnerRuntimeEntry { public string Prefab { get; set; } = ""; public string RuleId { get; set; } = ""; public List? Locations { get; set; } public ConditionsDefinition? Conditions { get; set; } public bool RuntimeReconcile { get; set; } public SpawnAreaDefinition? SpawnArea { get; set; } public CreatureSpawnerDefinition? CreatureSpawner { get; set; } } private sealed class CompiledSpawnerPrefabPlan { public List SpawnAreaEntries { get; } = new List(); public List DynamicSpawnAreaEntries { get; } = new List(); public List CreatureSpawnerEntries { get; } = new List(); public List DynamicCreatureSpawnerEntries { get; } = new List(); public HashSet SpawnAreaSelectorLocationKeys { get; } = new HashSet(StringComparer.OrdinalIgnoreCase); public HashSet CreatureSpawnerSelectorLocationKeys { get; } = new HashSet(StringComparer.OrdinalIgnoreCase); public bool HasUnscopedSpawnAreaEntries { get; set; } public bool HasUnscopedCreatureSpawnerEntries { get; set; } } private sealed class SpawnerRuntimeConfigurationSnapshot { public static SpawnerRuntimeConfigurationSnapshot Empty { get; } = new SpawnerRuntimeConfigurationSnapshot(); public Dictionary PlansByPrefab { get; } = new Dictionary(StringComparer.OrdinalIgnoreCase); public HashSet ConfiguredSpawnAreaPrefabs { get; } = new HashSet(StringComparer.OrdinalIgnoreCase); public HashSet ConfiguredCreatureSpawnerPrefabs { get; } = new HashSet(StringComparer.OrdinalIgnoreCase); public HashSet RuntimeConfiguredSpawnAreaPrefabs { get; } = new HashSet(StringComparer.OrdinalIgnoreCase); public HashSet RuntimeConfiguredCreatureSpawnerPrefabs { get; } = new HashSet(StringComparer.OrdinalIgnoreCase); } private sealed class SpawnAreaComponentCatalog { public string ConfigPrefabName { get; set; } = ""; public string RootPrefabName { get; set; } = ""; public string RelativePath { get; set; } = ""; } private sealed class CreatureSpawnerComponentCatalog { public string ConfigPrefabName { get; set; } = ""; public string RootPrefabName { get; set; } = ""; public string RelativePath { get; set; } = ""; } private sealed class SpawnerLocationProvenance { public int Epoch { get; set; } public string LocationPrefab { get; set; } = ""; public string RelativePath { get; set; } = ""; } private sealed class CurrentLocationSpawnContext { public string LocationPrefab { get; set; } = ""; } private sealed class SpawnerApplyOperations : StandardBaselineDesiredStateOperations { public static SpawnerApplyOperations Instance { get; } = new SpawnerApplyOperations(); public override string DomainKey => "spawner"; public override BaselineDesiredStateCapabilities Capabilities => BaselineDesiredStateCapabilities.Validation | BaselineDesiredStateCapabilities.LiveBaseline | BaselineDesiredStateCapabilities.LiveApply; public override void Validate(SpawnerDesiredState desiredState) { ValidateSpawnerDesiredState(desiredState); } public override void PrepareLiveBaseline(SpawnerDesiredState desiredState) { PrepareSpawnerLiveBaseline(desiredState); } public override void ApplyDesiredStateToLive(SpawnerDesiredState desiredState) { ApplySpawnerDesiredStateToLive(desiredState); } public override void Commit(SpawnerDesiredState desiredState) { RecordAppliedState(desiredState.GameDataSignature, desiredState.DomainEnabled, desiredState.CurrentEntrySignatures); } public override void HandleFailure(SpawnerDesiredState desiredState, bool liveStageFailed) { if (liveStageFailed && desiredState.ReloadPrefabs.Count != 0) { ReapplyOrQueueRegisteredLiveObjects(desiredState.DomainEnabled, desiredState.ReloadPrefabs); } } } private sealed class SpawnerDesiredState { public StandardDomainApplyPlan ApplyPlan { get; set; } public int GameDataSignature { get; set; } public Dictionary CurrentEntrySignatures { get; set; } = EmptyEntrySignatures; public bool DomainEnabled { get; set; } public bool QueueLiveReconcile { get; set; } public HashSet AvailablePrefabs { get; set; } = new HashSet(StringComparer.OrdinalIgnoreCase); public HashSet ReloadPrefabs { get; set; } = new HashSet(StringComparer.OrdinalIgnoreCase); public SpawnerRuntimeConfigurationSnapshot RuntimeConfigurationSnapshot { get; set; } = SpawnerRuntimeConfigurationSnapshot.Empty; } private sealed class SpawnerLiveReconcilerState { private readonly Dictionary _appliedCreatureSpawnerCheckIntervals = new Dictionary(); private readonly Dictionary _appliedCreatureSpawnerData = new Dictionary(); private readonly Dictionary _appliedCreatureSpawnerFaction = new Dictionary(); private readonly Dictionary _appliedCreatureSpawnerTimeOfDay = new Dictionary(); private readonly Dictionary> _appliedSpawnAreaPrefabsByInstance = new Dictionary>(); private readonly Dictionary _appliedSpawnAreaDataBySpawnData = new Dictionary(); private readonly Dictionary _appliedSpawnAreaFactionBySpawnData = new Dictionary(); private readonly Dictionary _appliedSpawnAreaTotalSpawnLimits = new Dictionary(); private readonly HashSet _pendingSpawnAreaTrackedAttempts = new HashSet(); private readonly Dictionary _pendingSpawnAreaSelections = new Dictionary(); private readonly Dictionary _pendingSpawnAreaSpawnPoints = new Dictionary(); private readonly Dictionary _pendingSpawnAreaSpawnedObjects = new Dictionary(); public void ResetPendingSpawnAreaAttempt(SpawnArea? spawnArea) { if (spawnArea != null) { _pendingSpawnAreaTrackedAttempts.Remove(spawnArea); _pendingSpawnAreaSelections.Remove(spawnArea); _pendingSpawnAreaSpawnPoints.Remove(spawnArea); _pendingSpawnAreaSpawnedObjects.Remove(spawnArea); } } public void BeginPendingSpawnAreaAttempt(SpawnArea spawnArea) { if (!((Object)(object)spawnArea == (Object)null)) { ResetPendingSpawnAreaAttempt(spawnArea); _pendingSpawnAreaTrackedAttempts.Add(spawnArea); } } public void RemovePendingSpawnAreaAttemptMarker(SpawnArea? spawnArea) { if (!((Object)(object)spawnArea == (Object)null)) { _pendingSpawnAreaTrackedAttempts.Remove(spawnArea); } } public bool HasPendingSpawnAreaAttempt(SpawnArea? spawnArea) { if ((Object)(object)spawnArea != (Object)null) { return _pendingSpawnAreaTrackedAttempts.Contains(spawnArea); } return false; } public void SetPendingSpawnAreaSelection(SpawnArea spawnArea, SpawnData? spawnData) { if (!((Object)(object)spawnArea == (Object)null)) { _pendingSpawnAreaSelections[spawnArea] = spawnData; } } public bool TryGetPendingSpawnAreaSelection(SpawnArea? spawnArea, out SpawnData? spawnData) { spawnData = null; if ((Object)(object)spawnArea != (Object)null) { return _pendingSpawnAreaSelections.TryGetValue(spawnArea, out spawnData); } return false; } public bool TryTakePendingSpawnAreaSelection(SpawnArea? spawnArea, out SpawnData? spawnData) { spawnData = null; if ((Object)(object)spawnArea == (Object)null || !_pendingSpawnAreaSelections.TryGetValue(spawnArea, out spawnData)) { return false; } _pendingSpawnAreaSelections.Remove(spawnArea); return true; } public void SetPendingSpawnAreaSpawnPoint(SpawnArea spawnArea, Vector3 spawnPoint) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)spawnArea == (Object)null)) { _pendingSpawnAreaSpawnPoints[spawnArea] = spawnPoint; } } public void RemovePendingSpawnAreaSpawnPoint(SpawnArea? spawnArea) { if (!((Object)(object)spawnArea == (Object)null)) { _pendingSpawnAreaSpawnPoints.Remove(spawnArea); } } public bool TryTakePendingSpawnAreaSpawnPoint(SpawnArea? spawnArea, out Vector3 spawnPoint) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) spawnPoint = default(Vector3); if ((Object)(object)spawnArea == (Object)null || !_pendingSpawnAreaSpawnPoints.TryGetValue(spawnArea, out spawnPoint)) { return false; } _pendingSpawnAreaSpawnPoints.Remove(spawnArea); return true; } public void SetPendingSpawnAreaSpawnedObject(SpawnArea spawnArea, GameObject spawnedObject) { if (!((Object)(object)spawnArea == (Object)null) && !((Object)(object)spawnedObject == (Object)null)) { _pendingSpawnAreaSpawnedObjects[spawnArea] = spawnedObject; } } public bool TryTakePendingSpawnAreaSpawnedObject(SpawnArea? spawnArea, out GameObject? spawnedObject) { spawnedObject = null; if ((Object)(object)spawnArea == (Object)null || !_pendingSpawnAreaSpawnedObjects.TryGetValue(spawnArea, out spawnedObject)) { return false; } _pendingSpawnAreaSpawnedObjects.Remove(spawnArea); return true; } public bool TryGetAppliedCreatureSpawnerData(CreatureSpawner? creatureSpawner, out ExpandWorldSpawnDataPayload payload) { if ((Object)(object)creatureSpawner != (Object)null && _appliedCreatureSpawnerData.TryGetValue(creatureSpawner, out ExpandWorldSpawnDataPayload value)) { payload = value; return true; } payload = null; return false; } public void SetAppliedCreatureSpawnerData(CreatureSpawner creatureSpawner, ExpandWorldSpawnDataPayload payload) { if (!((Object)(object)creatureSpawner == (Object)null) && payload != null) { _appliedCreatureSpawnerData[creatureSpawner] = payload; } } public void RemoveAppliedCreatureSpawnerData(CreatureSpawner? creatureSpawner) { if (creatureSpawner != null) { _appliedCreatureSpawnerData.Remove(creatureSpawner); } } public bool TryGetAppliedCreatureSpawnerFaction(CreatureSpawner? creatureSpawner, out string faction) { if ((Object)(object)creatureSpawner != (Object)null && _appliedCreatureSpawnerFaction.TryGetValue(creatureSpawner, out string value)) { faction = value; return true; } faction = ""; return false; } public void SetAppliedCreatureSpawnerFaction(CreatureSpawner creatureSpawner, string faction) { if (!((Object)(object)creatureSpawner == (Object)null) && !string.IsNullOrWhiteSpace(faction)) { _appliedCreatureSpawnerFaction[creatureSpawner] = faction; } } public void RemoveAppliedCreatureSpawnerFaction(CreatureSpawner? creatureSpawner) { if (creatureSpawner != null) { _appliedCreatureSpawnerFaction.Remove(creatureSpawner); } } public bool TryGetAppliedCreatureSpawnerTimeOfDay(CreatureSpawner? creatureSpawner, out TimeOfDayDefinition timeOfDay) { if ((Object)(object)creatureSpawner != (Object)null && _appliedCreatureSpawnerTimeOfDay.TryGetValue(creatureSpawner, out TimeOfDayDefinition value)) { timeOfDay = value; return true; } timeOfDay = null; return false; } public void SetAppliedCreatureSpawnerTimeOfDay(CreatureSpawner creatureSpawner, TimeOfDayDefinition timeOfDay) { if (!((Object)(object)creatureSpawner == (Object)null) && timeOfDay != null) { _appliedCreatureSpawnerTimeOfDay[creatureSpawner] = timeOfDay; } } public void RemoveAppliedCreatureSpawnerTimeOfDay(CreatureSpawner? creatureSpawner) { if (creatureSpawner != null) { _appliedCreatureSpawnerTimeOfDay.Remove(creatureSpawner); } } public void ClearAppliedCreatureSpawnerOverrides(CreatureSpawner? creatureSpawner) { RemoveAppliedCreatureSpawnerData(creatureSpawner); RemoveAppliedCreatureSpawnerFaction(creatureSpawner); RemoveAppliedCreatureSpawnerTimeOfDay(creatureSpawner); } public bool TryGetAppliedCreatureSpawnerCheckInterval(int instanceId, out int interval) { return _appliedCreatureSpawnerCheckIntervals.TryGetValue(instanceId, out interval); } public void SetAppliedCreatureSpawnerCheckInterval(int instanceId, int interval) { _appliedCreatureSpawnerCheckIntervals[instanceId] = interval; } public void RemoveAppliedCreatureSpawnerCheckInterval(int instanceId) { _appliedCreatureSpawnerCheckIntervals.Remove(instanceId); } public void SetAppliedSpawnAreaPrefabs(SpawnArea spawnArea, List prefabs) { if (!((Object)(object)spawnArea == (Object)null) && prefabs != null) { _appliedSpawnAreaPrefabsByInstance[spawnArea] = prefabs; } } public bool TryGetAppliedSpawnAreaPrefabs(SpawnArea? spawnArea, out List prefabs) { if ((Object)(object)spawnArea != (Object)null && _appliedSpawnAreaPrefabsByInstance.TryGetValue(spawnArea, out List value)) { prefabs = value; return true; } prefabs = null; return false; } public bool TryTakeAppliedSpawnAreaPrefabs(SpawnArea? spawnArea, out List prefabs) { if (spawnArea != null && _appliedSpawnAreaPrefabsByInstance.TryGetValue(spawnArea, out List value)) { prefabs = value; _appliedSpawnAreaPrefabsByInstance.Remove(spawnArea); return true; } prefabs = null; return false; } public bool TryGetAppliedSpawnAreaData(SpawnData? spawnData, out ExpandWorldSpawnDataPayload payload) { if (spawnData != null && _appliedSpawnAreaDataBySpawnData.TryGetValue(spawnData, out ExpandWorldSpawnDataPayload value)) { payload = value; return true; } payload = null; return false; } public void SetAppliedSpawnAreaData(SpawnData spawnData, ExpandWorldSpawnDataPayload payload) { if (spawnData != null && payload != null) { _appliedSpawnAreaDataBySpawnData[spawnData] = payload; } } public void RemoveAppliedSpawnAreaData(SpawnData? spawnData) { if (spawnData != null) { _appliedSpawnAreaDataBySpawnData.Remove(spawnData); } } public bool HasAppliedSpawnAreaData(SpawnData? spawnData) { if (spawnData != null) { return _appliedSpawnAreaDataBySpawnData.ContainsKey(spawnData); } return false; } public bool TryGetAppliedSpawnAreaFaction(SpawnData? spawnData, out string faction) { if (spawnData != null && _appliedSpawnAreaFactionBySpawnData.TryGetValue(spawnData, out string value)) { faction = value; return true; } faction = ""; return false; } public void SetAppliedSpawnAreaFaction(SpawnData spawnData, string faction) { if (spawnData != null && !string.IsNullOrWhiteSpace(faction)) { _appliedSpawnAreaFactionBySpawnData[spawnData] = faction; } } public void RemoveAppliedSpawnAreaFaction(SpawnData? spawnData) { if (spawnData != null) { _appliedSpawnAreaFactionBySpawnData.Remove(spawnData); } } public bool HasAppliedSpawnAreaFaction(SpawnData? spawnData) { if (spawnData != null) { return _appliedSpawnAreaFactionBySpawnData.ContainsKey(spawnData); } return false; } public bool TryGetAppliedSpawnAreaTotalSpawnLimit(SpawnArea? spawnArea, out SpawnAreaTotalSpawnLimitState state) { if ((Object)(object)spawnArea != (Object)null && _appliedSpawnAreaTotalSpawnLimits.TryGetValue(spawnArea, out state)) { return true; } state = default(SpawnAreaTotalSpawnLimitState); return false; } public void SetAppliedSpawnAreaTotalSpawnLimit(SpawnArea spawnArea, SpawnAreaTotalSpawnLimitState state) { if (!((Object)(object)spawnArea == (Object)null)) { _appliedSpawnAreaTotalSpawnLimits[spawnArea] = state; } } public void RemoveAppliedSpawnAreaTotalSpawnLimit(SpawnArea? spawnArea) { if (spawnArea != null) { _appliedSpawnAreaTotalSpawnLimits.Remove(spawnArea); } } public void Clear() { _appliedCreatureSpawnerCheckIntervals.Clear(); _appliedCreatureSpawnerData.Clear(); _appliedCreatureSpawnerFaction.Clear(); _appliedCreatureSpawnerTimeOfDay.Clear(); _appliedSpawnAreaPrefabsByInstance.Clear(); _appliedSpawnAreaDataBySpawnData.Clear(); _appliedSpawnAreaFactionBySpawnData.Clear(); _appliedSpawnAreaTotalSpawnLimits.Clear(); _pendingSpawnAreaTrackedAttempts.Clear(); _pendingSpawnAreaSelections.Clear(); _pendingSpawnAreaSpawnPoints.Clear(); _pendingSpawnAreaSpawnedObjects.Clear(); } } private readonly struct TrackedSpawnerPrefabState { public string PrefabName { get; } public int GameObjectInstanceId { get; } public bool ConfiguredEligible { get; } public bool RuntimeEligible { get; } public int EligibilityEpoch { get; } public TrackedSpawnerPrefabState(string prefabName, int gameObjectInstanceId) { PrefabName = prefabName; GameObjectInstanceId = gameObjectInstanceId; ConfiguredEligible = false; RuntimeEligible = false; EligibilityEpoch = int.MinValue; } private TrackedSpawnerPrefabState(string prefabName, int gameObjectInstanceId, bool configuredEligible, bool runtimeEligible, int eligibilityEpoch) { PrefabName = prefabName; GameObjectInstanceId = gameObjectInstanceId; ConfiguredEligible = configuredEligible; RuntimeEligible = runtimeEligible; EligibilityEpoch = eligibilityEpoch; } public TrackedSpawnerPrefabState WithEligibility(bool configuredEligible, bool runtimeEligible, int eligibilityEpoch) { return new TrackedSpawnerPrefabState(PrefabName, GameObjectInstanceId, configuredEligible, runtimeEligible, eligibilityEpoch); } } private sealed class SpawnerLiveRegistryStore { private readonly Dictionary _liveSpawnAreaSnapshots = new Dictionary(); private readonly Dictionary _liveCreatureSpawnerSnapshots = new Dictionary(); private readonly Dictionary> _liveSpawnAreasByPrefab = new Dictionary>(StringComparer.OrdinalIgnoreCase); private readonly Dictionary> _liveSpawnAreasByPrefabAndLocation = new Dictionary>(StringComparer.OrdinalIgnoreCase); private readonly Dictionary _liveSpawnAreaPrefabStates = new Dictionary(); private readonly Dictionary _spawnAreaLocationBucketByInstance = new Dictionary(); private readonly Dictionary> _liveCreatureSpawnersByPrefab = new Dictionary>(StringComparer.OrdinalIgnoreCase); private readonly Dictionary> _liveCreatureSpawnersByPrefabAndLocation = new Dictionary>(StringComparer.OrdinalIgnoreCase); private readonly Dictionary _liveCreatureSpawnerPrefabStates = new Dictionary(); private readonly Dictionary _creatureSpawnerLocationBucketByInstance = new Dictionary(); public bool TryGetTrackedPrefabName(SpawnArea? spawnArea, out string prefabName) { if ((Object)(object)spawnArea != (Object)null && _liveSpawnAreaPrefabStates.TryGetValue(spawnArea, out var value)) { prefabName = value.PrefabName; return true; } prefabName = ""; return false; } public bool TryGetTrackedPrefabName(CreatureSpawner? creatureSpawner, out string prefabName) { if ((Object)(object)creatureSpawner != (Object)null && _liveCreatureSpawnerPrefabStates.TryGetValue(creatureSpawner, out var value)) { prefabName = value.PrefabName; return true; } prefabName = ""; return false; } public bool TryGetTrackedState(SpawnArea? spawnArea, out TrackedSpawnerPrefabState trackedState) { if ((Object)(object)spawnArea != (Object)null && _liveSpawnAreaPrefabStates.TryGetValue(spawnArea, out trackedState)) { return true; } trackedState = default(TrackedSpawnerPrefabState); return false; } public bool TryGetTrackedState(CreatureSpawner? creatureSpawner, out TrackedSpawnerPrefabState trackedState) { if ((Object)(object)creatureSpawner != (Object)null && _liveCreatureSpawnerPrefabStates.TryGetValue(creatureSpawner, out trackedState)) { return true; } trackedState = default(TrackedSpawnerPrefabState); return false; } public bool HasLiveSnapshot(SpawnArea? spawnArea) { if ((Object)(object)spawnArea != (Object)null) { return _liveSpawnAreaSnapshots.ContainsKey(spawnArea); } return false; } public bool HasLiveSnapshot(CreatureSpawner? creatureSpawner) { if ((Object)(object)creatureSpawner != (Object)null) { return _liveCreatureSpawnerSnapshots.ContainsKey(creatureSpawner); } return false; } public void CaptureLiveSnapshot(SpawnArea spawnArea, SpawnAreaLiveSnapshot snapshot) { if (!((Object)(object)spawnArea == (Object)null)) { _liveSpawnAreaSnapshots[spawnArea] = snapshot; } } public void CaptureLiveSnapshot(CreatureSpawner creatureSpawner, CreatureSpawnerLiveSnapshot snapshot) { if (!((Object)(object)creatureSpawner == (Object)null)) { _liveCreatureSpawnerSnapshots[creatureSpawner] = snapshot; } } public bool TryGetLiveSnapshot(SpawnArea spawnArea, out SpawnAreaLiveSnapshot snapshot) { return _liveSpawnAreaSnapshots.TryGetValue(spawnArea, out snapshot); } public bool TryGetLiveSnapshot(CreatureSpawner creatureSpawner, out CreatureSpawnerLiveSnapshot snapshot) { return _liveCreatureSpawnerSnapshots.TryGetValue(creatureSpawner, out snapshot); } public void RemoveLiveSnapshot(SpawnArea? spawnArea) { if (spawnArea != null) { _liveSpawnAreaSnapshots.Remove(spawnArea); } } public void RemoveLiveSnapshot(CreatureSpawner? creatureSpawner) { if (creatureSpawner != null) { _liveCreatureSpawnerSnapshots.Remove(creatureSpawner); } } public void TrackSpawnAreaPrefab(SpawnArea spawnArea, string prefabName) { if (!((Object)(object)spawnArea == (Object)null) && !string.IsNullOrWhiteSpace(prefabName)) { _liveSpawnAreaPrefabStates[spawnArea] = new TrackedSpawnerPrefabState(prefabName, ((Object)((Component)spawnArea).gameObject).GetInstanceID()); if (!_liveSpawnAreasByPrefab.TryGetValue(prefabName, out HashSet value)) { value = new HashSet(); _liveSpawnAreasByPrefab[prefabName] = value; } value.Add(spawnArea); } } public void TrackCreatureSpawnerPrefab(CreatureSpawner creatureSpawner, string prefabName) { if (!((Object)(object)creatureSpawner == (Object)null) && !string.IsNullOrWhiteSpace(prefabName)) { _liveCreatureSpawnerPrefabStates[creatureSpawner] = new TrackedSpawnerPrefabState(prefabName, ((Object)((Component)creatureSpawner).gameObject).GetInstanceID()); if (!_liveCreatureSpawnersByPrefab.TryGetValue(prefabName, out HashSet value)) { value = new HashSet(); _liveCreatureSpawnersByPrefab[prefabName] = value; } value.Add(creatureSpawner); } } public bool UntrackSpawnAreaPrefab(SpawnArea? spawnArea, out string prefabName, out int gameObjectInstanceId) { prefabName = ""; gameObjectInstanceId = 0; if (spawnArea == null || !_liveSpawnAreaPrefabStates.Remove(spawnArea, out var value)) { return false; } prefabName = value.PrefabName; gameObjectInstanceId = value.GameObjectInstanceId; if (_liveSpawnAreasByPrefab.TryGetValue(prefabName, out HashSet value2)) { value2.Remove(spawnArea); if (value2.Count == 0) { _liveSpawnAreasByPrefab.Remove(prefabName); } } return true; } public bool UntrackCreatureSpawnerPrefab(CreatureSpawner? creatureSpawner, out string prefabName, out int gameObjectInstanceId) { prefabName = ""; gameObjectInstanceId = 0; if (creatureSpawner == null || !_liveCreatureSpawnerPrefabStates.Remove(creatureSpawner, out var value)) { return false; } prefabName = value.PrefabName; gameObjectInstanceId = value.GameObjectInstanceId; if (_liveCreatureSpawnersByPrefab.TryGetValue(prefabName, out HashSet value2)) { value2.Remove(creatureSpawner); if (value2.Count == 0) { _liveCreatureSpawnersByPrefab.Remove(prefabName); } } return true; } public void CollectDeadSpawnAreas(List target) { target.Clear(); foreach (KeyValuePair liveSpawnAreaPrefabState in _liveSpawnAreaPrefabStates) { if ((Object)(object)liveSpawnAreaPrefabState.Key == (Object)null || (Object)(object)((Component)liveSpawnAreaPrefabState.Key).gameObject == (Object)null) { target.Add(liveSpawnAreaPrefabState.Key); } } } public void CollectDeadCreatureSpawners(List target) { target.Clear(); foreach (KeyValuePair liveCreatureSpawnerPrefabState in _liveCreatureSpawnerPrefabStates) { if ((Object)(object)liveCreatureSpawnerPrefabState.Key == (Object)null || (Object)(object)((Component)liveCreatureSpawnerPrefabState.Key).gameObject == (Object)null) { target.Add(liveCreatureSpawnerPrefabState.Key); } } } public void ForEachTrackedSpawnArea(Action visitor) { foreach (KeyValuePair liveSpawnAreaPrefabState in _liveSpawnAreaPrefabStates) { if ((Object)(object)liveSpawnAreaPrefabState.Key != (Object)null) { visitor(liveSpawnAreaPrefabState.Key, liveSpawnAreaPrefabState.Value.PrefabName); } } } public void ForEachTrackedCreatureSpawner(Action visitor) { foreach (KeyValuePair liveCreatureSpawnerPrefabState in _liveCreatureSpawnerPrefabStates) { if ((Object)(object)liveCreatureSpawnerPrefabState.Key != (Object)null) { visitor(liveCreatureSpawnerPrefabState.Key, liveCreatureSpawnerPrefabState.Value.PrefabName); } } } public void UpdateTrackedEligibility(SpawnArea spawnArea, bool configuredEligible, bool runtimeEligible, int eligibilityEpoch) { if (!((Object)(object)spawnArea == (Object)null) && _liveSpawnAreaPrefabStates.TryGetValue(spawnArea, out var value)) { _liveSpawnAreaPrefabStates[spawnArea] = value.WithEligibility(configuredEligible, runtimeEligible, eligibilityEpoch); } } public void UpdateTrackedEligibility(CreatureSpawner creatureSpawner, bool configuredEligible, bool runtimeEligible, int eligibilityEpoch) { if (!((Object)(object)creatureSpawner == (Object)null) && _liveCreatureSpawnerPrefabStates.TryGetValue(creatureSpawner, out var value)) { _liveCreatureSpawnerPrefabStates[creatureSpawner] = value.WithEligibility(configuredEligible, runtimeEligible, eligibilityEpoch); } } public void AppendTrackedSpawnAreasForPrefab(string prefabName, List target) { if (!_liveSpawnAreasByPrefab.TryGetValue(prefabName, out HashSet value)) { return; } foreach (SpawnArea item in value) { if ((Object)(object)item != (Object)null && (Object)(object)((Component)item).gameObject != (Object)null) { target.Add(item); } } } public void AppendTrackedCreatureSpawnersForPrefab(string prefabName, List target) { if (!_liveCreatureSpawnersByPrefab.TryGetValue(prefabName, out HashSet value)) { return; } foreach (CreatureSpawner item in value) { if ((Object)(object)item != (Object)null && (Object)(object)((Component)item).gameObject != (Object)null) { target.Add(item); } } } public bool AppendTrackedSpawnAreasForBucket(string bucketKey, List target) { if (!_liveSpawnAreasByPrefabAndLocation.TryGetValue(bucketKey, out HashSet value)) { return false; } foreach (SpawnArea item in value) { if ((Object)(object)item != (Object)null && (Object)(object)((Component)item).gameObject != (Object)null) { target.Add(item); } } return true; } public bool AppendTrackedCreatureSpawnersForBucket(string bucketKey, List target) { if (!_liveCreatureSpawnersByPrefabAndLocation.TryGetValue(bucketKey, out HashSet value)) { return false; } foreach (CreatureSpawner item in value) { if ((Object)(object)item != (Object)null && (Object)(object)((Component)item).gameObject != (Object)null) { target.Add(item); } } return true; } public void UpdateSpawnAreaLocationBucket(SpawnArea spawnArea, string nextBucketKey) { if (!((Object)(object)spawnArea == (Object)null) && !string.IsNullOrWhiteSpace(nextBucketKey) && (!_spawnAreaLocationBucketByInstance.TryGetValue(spawnArea, out string value) || !string.Equals(value, nextBucketKey, StringComparison.OrdinalIgnoreCase))) { RemoveSpawnAreaLocationBucket(spawnArea); if (!_liveSpawnAreasByPrefabAndLocation.TryGetValue(nextBucketKey, out HashSet value2)) { value2 = new HashSet(); _liveSpawnAreasByPrefabAndLocation[nextBucketKey] = value2; } value2.Add(spawnArea); _spawnAreaLocationBucketByInstance[spawnArea] = nextBucketKey; } } public void UpdateCreatureSpawnerLocationBucket(CreatureSpawner creatureSpawner, string nextBucketKey) { if (!((Object)(object)creatureSpawner == (Object)null) && !string.IsNullOrWhiteSpace(nextBucketKey) && (!_creatureSpawnerLocationBucketByInstance.TryGetValue(creatureSpawner, out string value) || !string.Equals(value, nextBucketKey, StringComparison.OrdinalIgnoreCase))) { RemoveCreatureSpawnerLocationBucket(creatureSpawner); if (!_liveCreatureSpawnersByPrefabAndLocation.TryGetValue(nextBucketKey, out HashSet value2)) { value2 = new HashSet(); _liveCreatureSpawnersByPrefabAndLocation[nextBucketKey] = value2; } value2.Add(creatureSpawner); _creatureSpawnerLocationBucketByInstance[creatureSpawner] = nextBucketKey; } } public void RemoveSpawnAreaLocationBucket(SpawnArea? spawnArea) { if (spawnArea != null && _spawnAreaLocationBucketByInstance.Remove(spawnArea, out string value) && _liveSpawnAreasByPrefabAndLocation.TryGetValue(value, out HashSet value2)) { value2.Remove(spawnArea); if (value2.Count == 0) { _liveSpawnAreasByPrefabAndLocation.Remove(value); } } } public void RemoveCreatureSpawnerLocationBucket(CreatureSpawner? creatureSpawner) { if (creatureSpawner != null && _creatureSpawnerLocationBucketByInstance.Remove(creatureSpawner, out string value) && _liveCreatureSpawnersByPrefabAndLocation.TryGetValue(value, out HashSet value2)) { value2.Remove(creatureSpawner); if (value2.Count == 0) { _liveCreatureSpawnersByPrefabAndLocation.Remove(value); } } } public void ClearRuntimeView() { _liveSpawnAreaSnapshots.Clear(); _liveCreatureSpawnerSnapshots.Clear(); ClearLocationBuckets(); } public void ClearLocationBuckets() { _liveSpawnAreasByPrefabAndLocation.Clear(); _spawnAreaLocationBucketByInstance.Clear(); _liveCreatureSpawnersByPrefabAndLocation.Clear(); _creatureSpawnerLocationBucketByInstance.Clear(); } public void ClearLiveRegistries() { _liveSpawnAreasByPrefab.Clear(); _liveSpawnAreaPrefabStates.Clear(); _liveCreatureSpawnersByPrefab.Clear(); _liveCreatureSpawnerPrefabStates.Clear(); } } private sealed class PendingLocationRootProvenanceScan { public int RootInstanceId { get; set; } public Transform RootTransform { get; set; } public string LocationPrefab { get; set; } = ""; public int Epoch { get; set; } public List? TraversalStack { get; set; } } private sealed class SpawnerProvenanceRegistry { private readonly Dictionary _spawnAreaLocationProvenance = new Dictionary(); private readonly Dictionary _creatureSpawnerLocationProvenance = new Dictionary(); private readonly Dictionary _spawnedLocationRootPrefabsByTransform = new Dictionary(); private readonly List _currentLocationSpawnContexts = new List(); private readonly RingBufferQueue _pendingLocationRootProvenanceScans = new RingBufferQueue(); private readonly HashSet _pendingLocationRootProvenanceScanIds = new HashSet(); private int _nextLocationProvenanceEpoch = 1; public void PushLocationSpawnContext(string? locationPrefab) { string locationPrefab2 = (locationPrefab ?? "").Trim(); _currentLocationSpawnContexts.Add(new CurrentLocationSpawnContext { LocationPrefab = locationPrefab2 }); } public void PopLocationSpawnContext() { if (_currentLocationSpawnContexts.Count != 0) { _currentLocationSpawnContexts.RemoveAt(_currentLocationSpawnContexts.Count - 1); } } public bool TryGetActiveLocationSpawnContextPrefab(out string locationPrefab) { locationPrefab = ""; for (int num = _currentLocationSpawnContexts.Count - 1; num >= 0; num--) { string text = (_currentLocationSpawnContexts[num].LocationPrefab ?? "").Trim(); if (text.Length != 0) { locationPrefab = text; return true; } } return false; } public bool TryQueueRootProvenanceScan(Transform? rootTransform, string? locationPrefab, int epoch) { string text = (locationPrefab ?? "").Trim(); if ((Object)(object)rootTransform == (Object)null || text.Length == 0) { return false; } int instanceID = ((Object)rootTransform).GetInstanceID(); if (!_pendingLocationRootProvenanceScanIds.Add(instanceID)) { return false; } _pendingLocationRootProvenanceScans.Enqueue(new PendingLocationRootProvenanceScan { RootInstanceId = instanceID, RootTransform = rootTransform, LocationPrefab = text, Epoch = epoch }); return true; } public bool HasPendingRootScans() { return _pendingLocationRootProvenanceScans.Count > 0; } public bool TryPeekPendingRootScan(out PendingLocationRootProvenanceScan pendingScan) { if (_pendingLocationRootProvenanceScans.TryPeek(out PendingLocationRootProvenanceScan item)) { pendingScan = item; return true; } pendingScan = null; return false; } public void DiscardPendingRootScan(PendingLocationRootProvenanceScan pendingScan) { if (pendingScan != null) { _pendingLocationRootProvenanceScans.TryDequeue(out PendingLocationRootProvenanceScan _); _pendingLocationRootProvenanceScanIds.Remove(pendingScan.RootInstanceId); } } public void ClearPendingRootScans() { _pendingLocationRootProvenanceScans.Clear(); _pendingLocationRootProvenanceScanIds.Clear(); } public int AllocateLocationProvenanceEpoch() { if (_nextLocationProvenanceEpoch == int.MaxValue) { _nextLocationProvenanceEpoch = 1; } return _nextLocationProvenanceEpoch++; } public void RecordSpawnedLocationRoot(Transform rootTransform, string locationPrefab) { if (!((Object)(object)rootTransform == (Object)null) && !string.IsNullOrWhiteSpace(locationPrefab)) { _spawnedLocationRootPrefabsByTransform[rootTransform] = locationPrefab; } } public void RemoveSpawnedLocationRoot(Transform? rootTransform) { if (!((Object)(object)rootTransform == (Object)null)) { _spawnedLocationRootPrefabsByTransform.Remove(rootTransform); } } public bool TryGetRecordedRootLocationPrefab(Transform? rootTransform, out string locationPrefab) { locationPrefab = ""; if ((Object)(object)rootTransform != (Object)null && _spawnedLocationRootPrefabsByTransform.TryGetValue(rootTransform, out locationPrefab)) { return !string.IsNullOrWhiteSpace(locationPrefab); } return false; } public bool TryFindRecordedLocationRoot(Transform? transform, out Transform? rootTransform, out string locationPrefab) { rootTransform = null; locationPrefab = ""; Transform val = transform; while ((Object)(object)val != (Object)null) { if (_spawnedLocationRootPrefabsByTransform.TryGetValue(val, out locationPrefab)) { rootTransform = val; return true; } val = val.parent; } return false; } public bool HasSpawnAreaProvenance(SpawnArea? spawnArea) { if ((Object)(object)spawnArea != (Object)null) { return _spawnAreaLocationProvenance.ContainsKey(spawnArea); } return false; } public bool TryGetSpawnAreaProvenance(SpawnArea? spawnArea, out SpawnerLocationProvenance provenance) { if ((Object)(object)spawnArea != (Object)null && _spawnAreaLocationProvenance.TryGetValue(spawnArea, out SpawnerLocationProvenance value)) { provenance = value; return true; } provenance = null; return false; } public void RecordSpawnAreaProvenance(SpawnArea spawnArea, SpawnerLocationProvenance provenance) { if (!((Object)(object)spawnArea == (Object)null) && provenance != null) { _spawnAreaLocationProvenance[spawnArea] = provenance; } } public void RemoveSpawnAreaProvenance(SpawnArea? spawnArea) { if (spawnArea != null) { _spawnAreaLocationProvenance.Remove(spawnArea); } } public bool HasCreatureSpawnerProvenance(CreatureSpawner? creatureSpawner) { if ((Object)(object)creatureSpawner != (Object)null) { return _creatureSpawnerLocationProvenance.ContainsKey(creatureSpawner); } return false; } public bool TryGetCreatureSpawnerProvenance(CreatureSpawner? creatureSpawner, out SpawnerLocationProvenance provenance) { if ((Object)(object)creatureSpawner != (Object)null && _creatureSpawnerLocationProvenance.TryGetValue(creatureSpawner, out SpawnerLocationProvenance value)) { provenance = value; return true; } provenance = null; return false; } public void RecordCreatureSpawnerProvenance(CreatureSpawner creatureSpawner, SpawnerLocationProvenance provenance) { if (!((Object)(object)creatureSpawner == (Object)null) && provenance != null) { _creatureSpawnerLocationProvenance[creatureSpawner] = provenance; } } public void RemoveCreatureSpawnerProvenance(CreatureSpawner? creatureSpawner) { if (creatureSpawner != null) { _creatureSpawnerLocationProvenance.Remove(creatureSpawner); } } public void Clear(bool clearCurrentContexts) { _spawnAreaLocationProvenance.Clear(); _creatureSpawnerLocationProvenance.Clear(); _spawnedLocationRootPrefabsByTransform.Clear(); ClearPendingRootScans(); if (clearCurrentContexts) { _currentLocationSpawnContexts.Clear(); } _nextLocationProvenanceEpoch = 1; } } private sealed class SpawnerReconcileQueue { private readonly InstanceReconcileQueue _pendingSpawnAreaReconciles = new InstanceReconcileQueue((SpawnArea spawnArea, int instanceId, int epoch) => new PendingSpawnAreaReconcile(spawnArea, instanceId, epoch), (PendingSpawnAreaReconcile queuedSpawnArea) => queuedSpawnArea.InstanceId, (PendingSpawnAreaReconcile queuedSpawnArea) => queuedSpawnArea.Epoch, (PendingSpawnAreaReconcile queuedSpawnArea) => queuedSpawnArea.SpawnArea); private readonly InstanceReconcileQueue _pendingCreatureSpawnerReconciles = new InstanceReconcileQueue((CreatureSpawner creatureSpawner, int instanceId, int epoch) => new PendingCreatureSpawnerReconcile(creatureSpawner, instanceId, epoch), (PendingCreatureSpawnerReconcile queuedCreatureSpawner) => queuedCreatureSpawner.InstanceId, (PendingCreatureSpawnerReconcile queuedCreatureSpawner) => queuedCreatureSpawner.Epoch, (PendingCreatureSpawnerReconcile queuedCreatureSpawner) => queuedCreatureSpawner.CreatureSpawner); public bool TryQueue(SpawnArea? spawnArea, int epoch) { return _pendingSpawnAreaReconciles.TryQueue(spawnArea, epoch); } public bool TryQueue(CreatureSpawner? creatureSpawner, int epoch) { return _pendingCreatureSpawnerReconciles.TryQueue(creatureSpawner, epoch); } public bool HasPendingWork() { if (!_pendingSpawnAreaReconciles.HasPendingWork) { return _pendingCreatureSpawnerReconciles.HasPendingWork; } return true; } public bool TryDequeueNextSpawnArea(int epoch, out SpawnArea? spawnArea) { int instanceId; return _pendingSpawnAreaReconciles.TryDequeueCurrent(epoch, out spawnArea, out instanceId); } public bool TryDequeueNextCreatureSpawner(int epoch, out CreatureSpawner? creatureSpawner) { int instanceId; return _pendingCreatureSpawnerReconciles.TryDequeueCurrent(epoch, out creatureSpawner, out instanceId); } public void Clear() { _pendingSpawnAreaReconciles.Clear(); _pendingCreatureSpawnerReconciles.Clear(); } } private sealed class TemplateAggregate { public string Prefab { get; set; } = ""; public string OwnerName { get; set; } = "Unknown / Untracked"; public HashSet RootPrefabs { get; } = new HashSet(StringComparer.OrdinalIgnoreCase); public HashSet LocationPrefabs { get; } = new HashSet(StringComparer.OrdinalIgnoreCase); public SpawnAreaComponentSnapshot? SpawnArea { get; set; } public CreatureSpawnerComponentSnapshot? CreatureSpawner { get; set; } } private sealed class SpawnerRuntimeStateStore { private readonly Dictionary _runtimeSpawnAreaSignatures = new Dictionary(); private readonly Dictionary _runtimeCreatureSpawnerSignatures = new Dictionary(); private readonly Dictionary _spawnAreaLocalRuntimeStates = new Dictionary(); private readonly Dictionary _creatureSpawnerLocalRuntimeStates = new Dictionary(); private RuntimeContextSnapshot? _runtimeContextSnapshot; public bool HasRuntimeSignature(SpawnArea spawnArea) { if ((Object)(object)spawnArea != (Object)null) { return _runtimeSpawnAreaSignatures.ContainsKey(spawnArea); } return false; } public bool HasRuntimeSignature(CreatureSpawner creatureSpawner) { if ((Object)(object)creatureSpawner != (Object)null) { return _runtimeCreatureSpawnerSignatures.ContainsKey(creatureSpawner); } return false; } public bool TryGetRuntimeSignature(SpawnArea spawnArea, out int signature) { if ((Object)(object)spawnArea != (Object)null && _runtimeSpawnAreaSignatures.TryGetValue(spawnArea, out signature)) { return true; } signature = 0; return false; } public bool TryGetRuntimeSignature(CreatureSpawner creatureSpawner, out int signature) { if ((Object)(object)creatureSpawner != (Object)null && _runtimeCreatureSpawnerSignatures.TryGetValue(creatureSpawner, out signature)) { return true; } signature = 0; return false; } public void SetRuntimeSignature(SpawnArea spawnArea, int signature) { if (!((Object)(object)spawnArea == (Object)null)) { _runtimeSpawnAreaSignatures[spawnArea] = signature; } } public void SetRuntimeSignature(CreatureSpawner creatureSpawner, int signature) { if (!((Object)(object)creatureSpawner == (Object)null)) { _runtimeCreatureSpawnerSignatures[creatureSpawner] = signature; } } public void RemoveRuntimeSignature(SpawnArea? spawnArea) { if (spawnArea != null) { _runtimeSpawnAreaSignatures.Remove(spawnArea); } } public void RemoveRuntimeSignature(CreatureSpawner? creatureSpawner) { if (creatureSpawner != null) { _runtimeCreatureSpawnerSignatures.Remove(creatureSpawner); } } public LocalRuntimeState GetOrCreateLocalRuntimeState(SpawnArea spawnArea) { if (!_spawnAreaLocalRuntimeStates.TryGetValue(spawnArea, out LocalRuntimeState value)) { value = new LocalRuntimeState(); _spawnAreaLocalRuntimeStates[spawnArea] = value; } return value; } public LocalRuntimeState GetOrCreateLocalRuntimeState(CreatureSpawner creatureSpawner) { if (!_creatureSpawnerLocalRuntimeStates.TryGetValue(creatureSpawner, out LocalRuntimeState value)) { value = new LocalRuntimeState(); _creatureSpawnerLocalRuntimeStates[creatureSpawner] = value; } return value; } public void RemoveLocalRuntimeState(SpawnArea? spawnArea) { if (spawnArea != null) { _spawnAreaLocalRuntimeStates.Remove(spawnArea); } } public void RemoveLocalRuntimeState(CreatureSpawner? creatureSpawner) { if (creatureSpawner != null) { _creatureSpawnerLocalRuntimeStates.Remove(creatureSpawner); } } public bool TryGetRuntimeContextSnapshot(int frame, out RuntimeContextSnapshot snapshot) { if (_runtimeContextSnapshot != null && _runtimeContextSnapshot.Frame == frame) { snapshot = _runtimeContextSnapshot; return true; } snapshot = null; return false; } public RuntimeContextSnapshot SetRuntimeContextSnapshot(RuntimeContextSnapshot snapshot) { _runtimeContextSnapshot = snapshot; return snapshot; } public void Clear() { _runtimeSpawnAreaSignatures.Clear(); _runtimeCreatureSpawnerSignatures.Clear(); _spawnAreaLocalRuntimeStates.Clear(); _creatureSpawnerLocalRuntimeStates.Clear(); _runtimeContextSnapshot = null; } public void ClearDynamicCaches() { _runtimeSpawnAreaSignatures.Clear(); _runtimeCreatureSpawnerSignatures.Clear(); _runtimeContextSnapshot = null; } } private sealed class SpawnerConfigurationRuntimeState { public List Configuration { get; set; } = new List(); public string ConfigurationSignature { get; set; } = ""; public List ActiveEntries { get; } = new List(); public Dictionary> ActiveEntriesByPrefab { get; } = new Dictionary>(StringComparer.OrdinalIgnoreCase); public HashSet ConfiguredSpawnAreaPrefabs { get; } = new HashSet(StringComparer.OrdinalIgnoreCase); public HashSet ConfiguredCreatureSpawnerPrefabs { get; } = new HashSet(StringComparer.OrdinalIgnoreCase); public HashSet RuntimeConfiguredSpawnAreaPrefabs { get; } = new HashSet(StringComparer.OrdinalIgnoreCase); public HashSet RuntimeConfiguredCreatureSpawnerPrefabs { get; } = new HashSet(StringComparer.OrdinalIgnoreCase); public Dictionary CurrentEntrySignaturesByPrefab { get; } = new Dictionary(StringComparer.OrdinalIgnoreCase); public void Reset() { Configuration = new List(); ConfigurationSignature = ""; ActiveEntries.Clear(); ActiveEntriesByPrefab.Clear(); ConfiguredSpawnAreaPrefabs.Clear(); ConfiguredCreatureSpawnerPrefabs.Clear(); RuntimeConfiguredSpawnAreaPrefabs.Clear(); RuntimeConfiguredCreatureSpawnerPrefabs.Clear(); CurrentEntrySignaturesByPrefab.Clear(); } } private sealed class SpawnerLiveRuntimeState { public Dictionary SpawnAreaCatalogsByExactKey { get; } = new Dictionary(StringComparer.OrdinalIgnoreCase); public Dictionary CreatureSpawnerCatalogsByExactKey { get; } = new Dictionary(StringComparer.OrdinalIgnoreCase); public int TrackedSpawnerEligibilityEpoch { get; private set; } public void ClearComponentCatalogs() { SpawnAreaCatalogsByExactKey.Clear(); CreatureSpawnerCatalogsByExactKey.Clear(); } public void InvalidateTrackedSpawnerEligibility() { TrackedSpawnerEligibilityEpoch++; if (TrackedSpawnerEligibilityEpoch == int.MinValue) { TrackedSpawnerEligibilityEpoch = 0; } } } private sealed class SpawnerSelectorCacheStore { private readonly Dictionary _matchingSpawnAreaEntriesByInstance = new Dictionary(); private readonly Dictionary _matchingCreatureSpawnerEntriesByInstance = new Dictionary(); private readonly Dictionary _sharedMatchingEntryTemplates = new Dictionary(StringComparer.Ordinal); private readonly Dictionary _staticSelectorContextsByInstance = new Dictionary(); public bool TryGetSpawnAreaEntryCache(SpawnArea? spawnArea, out MatchingEntryCache entryCache) { if ((Object)(object)spawnArea != (Object)null && _matchingSpawnAreaEntriesByInstance.TryGetValue(spawnArea, out MatchingEntryCache value)) { entryCache = value; return true; } entryCache = null; return false; } public void SetSpawnAreaEntryCache(SpawnArea spawnArea, MatchingEntryCache entryCache) { if (!((Object)(object)spawnArea == (Object)null) && entryCache != null) { _matchingSpawnAreaEntriesByInstance[spawnArea] = entryCache; } } public void RemoveSpawnAreaEntryCache(SpawnArea? spawnArea) { if (spawnArea != null) { _matchingSpawnAreaEntriesByInstance.Remove(spawnArea); } } public bool TryGetCreatureSpawnerEntryCache(CreatureSpawner? creatureSpawner, out MatchingEntryCache entryCache) { if ((Object)(object)creatureSpawner != (Object)null && _matchingCreatureSpawnerEntriesByInstance.TryGetValue(creatureSpawner, out MatchingEntryCache value)) { entryCache = value; return true; } entryCache = null; return false; } public void SetCreatureSpawnerEntryCache(CreatureSpawner creatureSpawner, MatchingEntryCache entryCache) { if (!((Object)(object)creatureSpawner == (Object)null) && entryCache != null) { _matchingCreatureSpawnerEntriesByInstance[creatureSpawner] = entryCache; } } public void RemoveCreatureSpawnerEntryCache(CreatureSpawner? creatureSpawner) { if (creatureSpawner != null) { _matchingCreatureSpawnerEntriesByInstance.Remove(creatureSpawner); } } public bool TryGetSharedMatchingEntryTemplate(string cacheKey, out SharedMatchingEntryTemplate sharedTemplate) { if (_sharedMatchingEntryTemplates.TryGetValue(cacheKey, out SharedMatchingEntryTemplate value)) { sharedTemplate = value; return true; } sharedTemplate = null; return false; } public void SetSharedMatchingEntryTemplate(string cacheKey, SharedMatchingEntryTemplate sharedTemplate) { if (!string.IsNullOrWhiteSpace(cacheKey) && sharedTemplate != null) { _sharedMatchingEntryTemplates[cacheKey] = sharedTemplate; } } public void ClearSharedMatchingEntryTemplates() { _sharedMatchingEntryTemplates.Clear(); } public bool TryGetReusableStaticSelectorContext(GameObject gameObject, bool usesLocationFields, out StaticSelectorContextSnapshot snapshot) { //IL_0024: 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) snapshot = null; if ((Object)(object)gameObject == (Object)null || !_staticSelectorContextsByInstance.TryGetValue(((Object)gameObject).GetInstanceID(), out StaticSelectorContextSnapshot value)) { return false; } if (value.Position != gameObject.transform.position) { return false; } if (!usesLocationFields) { snapshot = value; return true; } if (!value.HasRecordedLocationProvenanceEpoch || !TryGetRecordedLocationProvenanceEpoch(gameObject, out var provenanceEpoch) || value.RecordedLocationProvenanceEpoch != provenanceEpoch) { return false; } snapshot = value; return true; } public void StoreReusableStaticSelectorContext(GameObject gameObject, StaticSelectorContextSnapshot snapshot, bool storeForLocationFields) { if (!((Object)(object)gameObject == (Object)null) && snapshot != null && (!storeForLocationFields || snapshot.HasRecordedLocationProvenanceEpoch)) { _staticSelectorContextsByInstance[((Object)gameObject).GetInstanceID()] = snapshot; } } public void RemoveStaticSelectorContext(int gameObjectInstanceId) { if (gameObjectInstanceId != 0) { _staticSelectorContextsByInstance.Remove(gameObjectInstanceId); } } public void Clear() { _matchingSpawnAreaEntriesByInstance.Clear(); _matchingCreatureSpawnerEntriesByInstance.Clear(); _sharedMatchingEntryTemplates.Clear(); _staticSelectorContextsByInstance.Clear(); } } private sealed class StaticSelectorContextSnapshot { public Vector3 Position { get; set; } public bool HasRecordedLocationProvenanceEpoch { get; set; } public int RecordedLocationProvenanceEpoch { get; set; } public string ConditionLocationName { get; set; } = ""; public Biome Biome { get; set; } public bool InDungeon { get; set; } } private readonly struct SpawnAreaTotalSpawnLimitState { public int MaxTotalSpawns { get; } public bool FromYamlOverride { get; } public SpawnAreaTotalSpawnLimitState(int maxTotalSpawns, bool fromYamlOverride) { MaxTotalSpawns = SpawnerGlobalConfig.ClampSpawnAreaMaxTotalSpawns(maxTotalSpawns); FromYamlOverride = fromYamlOverride; } } private const string ReferenceAutoUpdateStateKey = "spawner"; private const string LocationReferenceAutoUpdateStateKey = "spawner.locations"; private const string UnresolvedSelectorLocationCacheKey = ""; private const float RuntimeEvaluationIntervalSeconds = 0.25f; private const float RuntimeEvaluationIntervalInsidePlayerBaseOnlySeconds = 0.5f; internal static readonly DomainModuleDefinition Module = new DomainModuleDefinition(new DomainModuleOptions { DomainKey = "spawner", ReloadDomain = DropNSpawnPlugin.ReloadDomain.Spawner, ManifestSettingKey = "spawner_yaml", ManifestPriority = 98, ShouldReloadForPath = ShouldReloadForPath, Reload = ReloadConfiguration, InitializeRuntime = Initialize, OnGameDataReady = OnGameDataReady, HandleExpandWorldDataReady = HandleExpandWorldDataReady, DtoVersion = 7, TransportProfile = DomainTransportProfile.MediumConfig, DisplayName = "spawner", CacheDirectoryName = "spawner", ClientRequestPriority = 30, KeySelector = (SpawnerConfigurationEntry entry) => entry.RuleId, ApplyPayloadAction = ApplySyncedPayload, HasPendingReconcileWork = HasPendingReconcileWork, ProcessPendingReconcileStep = ProcessQueuedReconcileStep, BeforeClientManifestChanged = MarkSyncedPayloadPending, OnClientAuthorityCutover = EnterPendingSyncedPayloadState }); private static readonly object Sync = new object(); private static readonly IDeserializer Deserializer = new DeserializerBuilder().WithNamingConvention(CamelCaseNamingConvention.Instance).Build(); private static readonly ISerializer Serializer = new SerializerBuilder().WithNamingConvention(CamelCaseNamingConvention.Instance).ConfigureDefaultValuesHandling(DefaultValuesHandling.OmitNull | DefaultValuesHandling.OmitDefaults).Build(); private static readonly List SpawnAreaSnapshots = new List(); private static readonly List CreatureSpawnerSnapshots = new List(); private static readonly Dictionary SpawnAreaSnapshotsByExactKey = new Dictionary(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary CreatureSpawnerSnapshotsByExactKey = new Dictionary(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary> SpawnAreaSnapshotsByName = new Dictionary>(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary> CreatureSpawnerSnapshotsByName = new Dictionary>(StringComparer.OrdinalIgnoreCase); private static readonly SpawnerConfigurationRuntimeState RuntimeState = new SpawnerConfigurationRuntimeState(); private static readonly InvalidEntryDiagnostics InvalidEntryWarnings = new InvalidEntryDiagnostics(); private static readonly SpawnerLiveRuntimeState LiveRuntimeState = new SpawnerLiveRuntimeState(); private static readonly HashSet CapturedRootPrefabNames = new HashSet(StringComparer.OrdinalIgnoreCase); private static readonly FieldInfo? CreatureSpawnerCheckedLocationField = AccessTools.Field(typeof(CreatureSpawner), "m_checkedLocation"); private static readonly FieldInfo? CreatureSpawnerLocationField = AccessTools.Field(typeof(CreatureSpawner), "m_location"); private static readonly FieldInfo? CreatureSpawnerSpawnGroupField = AccessTools.Field(typeof(CreatureSpawner), "m_spawnGroup"); private static bool _lastAppliedSynchronizedPayloadReady; private static bool _initialized; private static bool _snapshotsCaptured; private static int? _lastProcessedGameDataSignature; private static SpawnerRuntimeConfigurationSnapshot _runtimeConfigurationSnapshot = SpawnerRuntimeConfigurationSnapshot.Empty; private static bool _referenceArtifactsAutoRefreshConsumed; private static readonly Dictionary _lastAppliedEntrySignaturesByPrefab = new Dictionary(StringComparer.OrdinalIgnoreCase); private static string _lastAppliedConfigurationSignature = ""; private static int? _lastAppliedGameDataSignature; private static bool? _lastAppliedDomainEnabled; private static int _reconcileQueueEpoch; private const string MockPrefabPrefix = "JVLmock_"; private static bool _synchronizedPayloadReady; private static readonly DomainConfigurationRuntime ConfigurationRuntime = new DomainConfigurationRuntime(new DomainLoadHooks(ParseLocalConfigurationDocuments, BuildSyncedConfigurationState, CommitSyncedConfigurationState, RejectLocalConfigurationPayload, (SyncedSpawnerConfigurationState state) => state.Configuration.Count, LogPartiallyAcceptedLocalConfiguration, LogLocalConfigurationLoaded, OnSourceOfTruthPayloadUnchanged, delegate { ConfigurationDomainHost.PublishSyncedPayload(DropNSpawnPlugin.IsSourceOfTruth, Descriptor, RuntimeState.Configuration, RuntimeState.ConfigurationSignature); }), new DomainSyncHooks(delegate(out List configuration, out string payloadToken) { return ConfigurationDomainHost.TryGetSyncedEntries(Descriptor, out configuration, out payloadToken); }, (string payloadToken) => ConfigurationDomainHost.ShouldSkipSyncedPayload(LoadState, payloadToken, Volatile.Read(in _synchronizedPayloadReady)), BuildSyncedConfigurationState, CommitSyncedConfigurationState, (SyncedSpawnerConfigurationState state) => state.ActiveEntries.Count, "ServerSync:DropNSpawnSpawner", MarkSyncedPayloadPending, LogSyncedSpawnerConfigurationLoaded, LogSyncedSpawnerConfigurationFailure)); private static readonly Dictionary EmptyEntrySignatures = new Dictionary(StringComparer.OrdinalIgnoreCase); private static readonly SpawnerLiveReconcilerState LiveReconcilerState = new SpawnerLiveReconcilerState(); private static readonly SpawnerLiveRegistryStore LiveRegistryStore = new SpawnerLiveRegistryStore(); private static readonly int ExpandWorldDataLocationReferenceHash = StringExtensionMethods.GetStableHashCode("locationreference"); private static readonly int DropNSpawnLocationPrefabHash = StringExtensionMethods.GetStableHashCode("dropnspawn.locationPrefab"); private static bool _expandWorldDataCurrentLocationFieldResolved; private static FieldInfo? _expandWorldDataCurrentLocationField; private static readonly SpawnerProvenanceRegistry ProvenanceRegistry = new SpawnerProvenanceRegistry(); private static readonly SpawnerReconcileQueue ReconcileQueue = new SpawnerReconcileQueue(); private static readonly SpawnerRuntimeStateStore RuntimeStateStore = new SpawnerRuntimeStateStore(); private static readonly SpawnerSelectorCacheStore SelectorCacheStore = new SpawnerSelectorCacheStore(); private static readonly int SpawnAreaTotalSpawnCountZdoKey = StringExtensionMethods.GetStableHashCode("DropNSpawn.SpawnArea.TotalSpawnCount"); private static readonly int SpawnAreaMaxTotalSpawnsZdoKey = StringExtensionMethods.GetStableHashCode("DropNSpawn.SpawnArea.MaxTotalSpawns"); internal static DomainDescriptor Descriptor => Module.DescriptorTyped; internal static DomainTransportMetadata TransportMetadata => Module.TransportMetadataTyped; private static DomainLoadState LoadState => ConfigurationRuntime.LoadState; private static string ReferenceConfigurationPath => Path.Combine(DropNSpawnPlugin.YamlConfigDirectoryPath, PluginSettingsFacade.GetYamlDomainFilePrefix("spawner") + ".reference.yml"); private static string LocationReferenceConfigurationPath => Path.Combine(DropNSpawnPlugin.YamlConfigDirectoryPath, PluginSettingsFacade.GetYamlDomainFilePrefix("spawner") + ".locations.reference.yml"); private static string PrimaryOverrideConfigurationPathYml => Path.Combine(DropNSpawnPlugin.YamlConfigDirectoryPath, PluginSettingsFacade.GetYamlDomainFilePrefix("spawner") + ".yml"); private static string PrimaryOverrideConfigurationPathYaml => Path.Combine(DropNSpawnPlugin.YamlConfigDirectoryPath, PluginSettingsFacade.GetYamlDomainFilePrefix("spawner") + ".yaml"); private static string FullScaffoldConfigurationPath => Path.Combine(DropNSpawnPlugin.YamlConfigDirectoryPath, PluginSettingsFacade.GetYamlDomainFilePrefix("spawner") + ".full.yml"); internal static void RecordDirectSpawnAreaSpawnedObject(SpawnArea spawnArea, GameObject? spawnedObject) { lock (Sync) { if (!((Object)(object)spawnArea == (Object)null) && !((Object)(object)spawnedObject == (Object)null) && LiveReconcilerState.HasPendingSpawnAreaAttempt(spawnArea)) { LiveReconcilerState.SetPendingSpawnAreaSpawnedObject(spawnArea, spawnedObject); } } } internal static void FinalizeSpawnAreaSpawnAttempt(SpawnArea spawnArea, bool succeeded) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0121: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Unknown result type (might be due to invalid IL or missing references) Vector3 spawnPoint = default(Vector3); ExpandWorldSpawnDataPayload expandWorldSpawnDataPayload = null; Character val = null; string text = null; string text2 = null; lock (Sync) { LiveReconcilerState.RemovePendingSpawnAreaAttemptMarker(spawnArea); LiveReconcilerState.TryTakePendingSpawnAreaSelection(spawnArea, out SpawnData spawnData); Vector3 spawnPoint2; bool flag = LiveReconcilerState.TryTakePendingSpawnAreaSpawnPoint(spawnArea, out spawnPoint2); string faction = null; ExpandWorldSpawnDataPayload payload = null; bool flag2 = spawnData != null && LiveReconcilerState.TryGetAppliedSpawnAreaFaction(spawnData, out faction); bool flag3 = spawnData != null && LiveReconcilerState.TryGetAppliedSpawnAreaData(spawnData, out payload) && payload.HasObjects; if (succeeded && flag && spawnData != null && (flag2 || flag3)) { if (flag3) { spawnPoint = spawnPoint2; expandWorldSpawnDataPayload = payload; } if (flag2) { val = ((LiveReconcilerState.TryTakePendingSpawnAreaSpawnedObject(spawnArea, out GameObject spawnedObject) && (Object)(object)spawnedObject != (Object)null) ? spawnedObject.GetComponent() : null); if ((Object)(object)val != (Object)null) { text = faction; text2 = GetConfigPrefabName(((Component)spawnArea).gameObject, "SpawnArea") + "@" + DescribeInstance(((Component)spawnArea).gameObject) + "/spawnArea.spawn"; } } } if (succeeded) { RecordSuccessfulSpawnAreaTotalSpawn(spawnArea); } } if (expandWorldSpawnDataPayload != null) { ExpandWorldSpawnDataSupport.SpawnObjects(spawnPoint, expandWorldSpawnDataPayload); } if ((Object)(object)val != (Object)null && text != null && text2 != null) { FactionIntegration.Apply(val, text, text2); } } internal static void ApplyCreatureSpawnerSpawnOverrides(CreatureSpawner creatureSpawner, ZNetView? spawnedView) { //IL_0002: 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_00cc: Unknown result type (might be due to invalid IL or missing references) Vector3 spawnPoint = default(Vector3); ExpandWorldSpawnDataPayload expandWorldSpawnDataPayload = null; Character val = null; string text = null; string text2 = null; lock (Sync) { if ((Object)(object)creatureSpawner == (Object)null || (Object)(object)spawnedView == (Object)null) { return; } string text3 = GetConfigPrefabName(((Component)creatureSpawner).gameObject, "CreatureSpawner") + "@" + DescribeInstance(((Component)creatureSpawner).gameObject) + "/creatureSpawner.spawn"; if (LiveReconcilerState.TryGetAppliedCreatureSpawnerData(creatureSpawner, out ExpandWorldSpawnDataPayload payload) && payload.HasObjects) { spawnPoint = ((Component)spawnedView).transform.position; expandWorldSpawnDataPayload = payload; } Character component = ((Component)spawnedView).GetComponent(); if ((Object)(object)component == (Object)null) { return; } if (LiveReconcilerState.TryGetAppliedCreatureSpawnerFaction(creatureSpawner, out string faction)) { val = component; text = faction; text2 = text3; } } if (expandWorldSpawnDataPayload != null) { ExpandWorldSpawnDataSupport.SpawnObjects(spawnPoint, expandWorldSpawnDataPayload); } if ((Object)(object)val != (Object)null && text != null && text2 != null) { FactionIntegration.Apply(val, text, text2); } } internal static void InitializeSpawnAreaSpawnData(SpawnArea spawnArea, GameObject? prefab, Vector3 spawnPoint) { //IL_0055: Unknown result type (might be due to invalid IL or missing references) ExpandWorldSpawnDataPayload payload2; lock (Sync) { if ((Object)(object)spawnArea == (Object)null || (Object)(object)prefab == (Object)null || !LiveReconcilerState.TryGetPendingSpawnAreaSelection(spawnArea, out SpawnData spawnData) || spawnData == null || !LiveReconcilerState.TryGetAppliedSpawnAreaData(spawnData, out ExpandWorldSpawnDataPayload payload)) { return; } payload2 = payload; } ExpandWorldSpawnDataSupport.InitializeSpawn(prefab, spawnPoint, payload2); } internal static void InitializeCreatureSpawnerSpawnData(CreatureSpawner creatureSpawner, GameObject? prefab, Vector3 spawnPoint) { //IL_0042: Unknown result type (might be due to invalid IL or missing references) ExpandWorldSpawnDataPayload payload2; lock (Sync) { if ((Object)(object)creatureSpawner == (Object)null || (Object)(object)prefab == (Object)null || !LiveReconcilerState.TryGetAppliedCreatureSpawnerData(creatureSpawner, out ExpandWorldSpawnDataPayload payload)) { return; } payload2 = payload; } ExpandWorldSpawnDataSupport.InitializeSpawn(prefab, spawnPoint, payload2); } private static void ApplyDesiredStateToLiveObjects(SpawnerDesiredState desiredState) { if (desiredState.ReloadPrefabs.Count != 0) { if (desiredState.QueueLiveReconcile) { ReapplyOrQueueRegisteredLiveObjects(desiredState.DomainEnabled, desiredState.ReloadPrefabs, desiredState.RuntimeConfigurationSnapshot); } else { ReapplyRegisteredLiveObjects(desiredState.DomainEnabled, desiredState.ReloadPrefabs, desiredState.RuntimeConfigurationSnapshot); } } } private static void ReapplyRegisteredLiveObjects(bool domainEnabled, HashSet prefabs, SpawnerRuntimeConfigurationSnapshot runtimeConfigurationSnapshot) { string configPrefabName; foreach (SpawnArea registeredSpawnArea in GetRegisteredSpawnAreas(prefabs, runtimeConfigurationSnapshot)) { TrackSpawnAreaInstanceInternal(registeredSpawnArea); if (domainEnabled && TryGetActiveSpawnAreaEntries(registeredSpawnArea, runtimeConfigurationSnapshot, out IReadOnlyList entries, out configPrefabName)) { ReconcileSpawnAreaInstanceInternal(registeredSpawnArea, entries); } else { RestoreSpawnAreaInstance(registeredSpawnArea); } } foreach (CreatureSpawner registeredCreatureSpawner in GetRegisteredCreatureSpawners(prefabs, runtimeConfigurationSnapshot)) { TrackCreatureSpawnerInstanceInternal(registeredCreatureSpawner); if (domainEnabled && TryGetActiveCreatureSpawnerEntries(registeredCreatureSpawner, runtimeConfigurationSnapshot, out IReadOnlyList entries2, out configPrefabName)) { ReconcileCreatureSpawnerInstanceInternal(registeredCreatureSpawner, entries2); } else { RestoreCreatureSpawnerInstance(registeredCreatureSpawner, refreshRuntimeState: true); } } } private static void ReapplyOrQueueRegisteredLiveObjects(bool domainEnabled, HashSet prefabs, SpawnerRuntimeConfigurationSnapshot runtimeConfigurationSnapshot) { foreach (SpawnArea registeredSpawnArea in GetRegisteredSpawnAreas(prefabs, runtimeConfigurationSnapshot)) { TrackSpawnAreaInstanceInternal(registeredSpawnArea); if (domainEnabled && TryGetActiveSpawnAreaEntryCache(registeredSpawnArea, runtimeConfigurationSnapshot, out MatchingEntryCache entryCache, out string configPrefabName)) { if (runtimeConfigurationSnapshot.RuntimeConfiguredSpawnAreaPrefabs.Contains(configPrefabName)) { QueueSpawnAreaReconcile(registeredSpawnArea); } else { ReconcileSpawnAreaInstanceInternal(registeredSpawnArea, entryCache.Entries, entryCache); } } else { RestoreSpawnAreaInstance(registeredSpawnArea); } } foreach (CreatureSpawner registeredCreatureSpawner in GetRegisteredCreatureSpawners(prefabs, runtimeConfigurationSnapshot)) { TrackCreatureSpawnerInstanceInternal(registeredCreatureSpawner); if (domainEnabled && TryGetActiveCreatureSpawnerEntryCache(registeredCreatureSpawner, runtimeConfigurationSnapshot, out MatchingEntryCache entryCache2, out string configPrefabName2)) { if (runtimeConfigurationSnapshot.RuntimeConfiguredCreatureSpawnerPrefabs.Contains(configPrefabName2)) { QueueCreatureSpawnerReconcile(registeredCreatureSpawner); } else { ReconcileCreatureSpawnerInstanceInternal(registeredCreatureSpawner, entryCache2.Entries, entryCache2); } } else { RestoreCreatureSpawnerInstance(registeredCreatureSpawner, refreshRuntimeState: true); } } } private static bool TryGetActiveSpawnAreaEntries(SpawnArea? spawnArea, SpawnerRuntimeConfigurationSnapshot runtimeConfigurationSnapshot, out IReadOnlyList? entries, out string configPrefabName) { entries = null; if (!TryGetActiveSpawnAreaEntryCache(spawnArea, runtimeConfigurationSnapshot, out MatchingEntryCache entryCache, out configPrefabName)) { return false; } entries = entryCache.Entries; return true; } private static bool TryGetActiveCreatureSpawnerEntries(CreatureSpawner? creatureSpawner, SpawnerRuntimeConfigurationSnapshot runtimeConfigurationSnapshot, out IReadOnlyList? entries, out string configPrefabName) { entries = null; if (!TryGetActiveCreatureSpawnerEntryCache(creatureSpawner, runtimeConfigurationSnapshot, out MatchingEntryCache entryCache, out configPrefabName)) { return false; } entries = entryCache.Entries; return true; } internal static bool ShouldReloadForPath(string? path) { if (PluginSettingsFacade.IsEligibleOverrideConfigurationPath(path)) { return IsOverrideConfigurationFileName(Path.GetFileName(path ?? "")); } return false; } private static bool ShouldApplyLocally() { return PluginSettingsFacade.IsSpawnerDomainEnabled(); } internal static void MarkSyncedPayloadPending() { lock (Sync) { ConfigurationRuntime.MarkSyncedPayloadPending(DropNSpawnPlugin.IsSourceOfTruth, delegate { Volatile.Write(ref _synchronizedPayloadReady, value: false); }); } } internal static void EnterPendingSyncedPayloadState() { lock (Sync) { Dictionary previousEntrySignatures = CloneCurrentEntrySignaturesByPrefab(); HashSet previouslyAppliedPrefabs = BuildLastAppliedPrefabs(); ConfigurationRuntime.EnterPendingSyncedPayloadState(DropNSpawnPlugin.IsSourceOfTruth, ResetLoadedConfigurationState, delegate { RuntimeState.ConfigurationSignature = ""; _lastAppliedSynchronizedPayloadReady = false; ReapplyRegisteredLiveObjects(domainEnabled: false, previouslyAppliedPrefabs); RefreshVneiCompatibility(previousEntrySignatures); }); } } internal static bool ShouldBlockClientSpawnerUpdate() { if (!ShouldApplyLocally() || DropNSpawnPlugin.IsSourceOfTruth) { return false; } if (DropNSpawnPlugin.IsGameDataRefreshDeferred(DropNSpawnPlugin.ReloadDomain.Spawner)) { return true; } if (!IsGameDataReady()) { return true; } return !Volatile.Read(in _synchronizedPayloadReady); } internal static void Initialize() { lock (Sync) { if (!_initialized) { LoadConfiguration(); _initialized = true; } } } internal static void ReloadConfiguration() { lock (Sync) { LoadConfiguration(); ApplyIfReady(queueLiveReconcile: true); } } internal static bool HandleExpandWorldDataReady() { lock (Sync) { if (!DropNSpawnPlugin.IsSourceOfTruth) { return false; } string text = NetworkPayloadSyncSupport.ComputeSpawnerConfigurationSignature(RuntimeState.Configuration); if (string.Equals(text, RuntimeState.ConfigurationSignature, StringComparison.Ordinal)) { return false; } RuntimeState.ConfigurationSignature = text; ConfigurationDomainHost.PublishSyncedPayload(DropNSpawnPlugin.IsSourceOfTruth, Descriptor, RuntimeState.Configuration, RuntimeState.ConfigurationSignature); ApplyIfReady(queueLiveReconcile: true); return true; } } internal static void ApplySyncedPayload() { lock (Sync) { Dictionary previousEntrySignatures = CloneCurrentEntrySignaturesByPrefab(); ConfigurationRuntime.ApplySyncedPayload(delegate { RefreshVneiCompatibility(previousEntrySignatures, CloneCurrentEntrySignaturesByPrefab()); ApplyIfReady(queueLiveReconcile: true); }); } } internal static void OnGameDataReady(string source) { lock (Sync) { if (!_initialized) { Initialize(); } if (!IsGameDataReady()) { return; } int num = ComputeGameDataSignature(BuildCurrentSpawnerReferencePrefabKeys().ToHashSet(StringComparer.OrdinalIgnoreCase)); if (_lastProcessedGameDataSignature != num) { ResetReferenceSnapshots(); ResetRuntimeState(preserveLiveRegistries: true); CleanupRegisteredSpawnAreas(); CleanupRegisteredCreatureSpawners(); if (DropNSpawnPlugin.IsSourceOfTruth && !_referenceArtifactsAutoRefreshConsumed) { EnsureReferenceArtifactsUpToDate(); _referenceArtifactsAutoRefreshConsumed = true; } else if (!DropNSpawnPlugin.IsSourceOfTruth) { _referenceArtifactsAutoRefreshConsumed = true; } if (DropNSpawnPlugin.IsSourceOfTruth && EnsurePrimaryOverrideConfigurationFileExists()) { LoadConfiguration(); } ApplyIfReady(queueLiveReconcile: true); _lastProcessedGameDataSignature = num; } } } private static bool IsGameDataReady() { return (Object)(object)ZNetScene.instance != (Object)null; } private static int ComputeGameDataSignature(IEnumerable? availablePrefabs = null) { if (!IsGameDataReady() || (Object)(object)ZNetScene.instance == (Object)null) { return 0; } return HashNormalizedKeys(HashNormalizedKeys(17 * 31 + ((Object)ZNetScene.instance).GetInstanceID(), availablePrefabs ?? BuildCurrentSpawnerReferencePrefabKeys()), BuildConfiguredSpawnerResolutionKeys()); } private static int HashNormalizedKeys(int hash, IEnumerable keys) { foreach (string item in (from key in (keys ?? Enumerable.Empty()).Select(ReferenceRefreshSupport.NormalizeKey) where key.Length > 0 select key).OrderBy((string key) => key, StringComparer.OrdinalIgnoreCase)) { hash = hash * 31 + StringComparer.OrdinalIgnoreCase.GetHashCode(item); } return hash; } private static IEnumerable BuildConfiguredSpawnerResolutionKeys() { HashSet seen = new HashSet(StringComparer.OrdinalIgnoreCase); foreach (var (_, list2) in RuntimeState.ActiveEntriesByPrefab) { foreach (SpawnerConfigurationEntry entry in list2) { if (entry.SpawnArea?.Creatures != null) { for (int index = 0; index < entry.SpawnArea.Creatures.Count; index++) { string text2 = ReferenceRefreshSupport.NormalizeKey(entry.SpawnArea.Creatures[index]?.Creature); if (text2.Length != 0) { GameObject? obj = ResolveCreaturePrefabForSignature(text2); string text3 = "spawnArea:" + text2 + ":" + ((obj != null) ? ((Object)obj).GetInstanceID() : 0).ToString(CultureInfo.InvariantCulture); if (seen.Add(text3)) { yield return text3; } } } } string text4 = ReferenceRefreshSupport.NormalizeKey(entry.CreatureSpawner?.Creature); if (text4.Length != 0) { GameObject? obj2 = ResolveCreaturePrefabForSignature(text4); string text5 = "creatureSpawner:" + text4 + ":" + ((obj2 != null) ? ((Object)obj2).GetInstanceID() : 0).ToString(CultureInfo.InvariantCulture); if (seen.Add(text5)) { yield return text5; } } } } } private static GameObject? ResolveCreaturePrefabForSignature(string? prefabName) { string text = ReferenceRefreshSupport.NormalizeKey(prefabName); if (text.Length == 0) { return null; } ZNetScene instance = ZNetScene.instance; GameObject val = ((instance != null) ? instance.GetPrefab(text) : null); if ((Object)(object)val == (Object)null) { return null; } Character val2 = default(Character); BaseAI val3 = default(BaseAI); if (!val.TryGetComponent(ref val2) && !val.TryGetComponent(ref val3)) { return null; } return val; } private static bool EnsurePrimaryOverrideConfigurationFileExists() { if (DomainConfigurationFileSupport.HasAnyOverrideConfigurationFile("spawner", PrimaryOverrideConfigurationPathYml, PrimaryOverrideConfigurationPathYaml)) { return false; } GeneratedArtifactWriter.WriteTextAlways(PrimaryOverrideConfigurationPathYml, BuildPrimaryOverrideConfigurationTemplate(), "Created spawner override configuration at " + PrimaryOverrideConfigurationPathYml + "."); return true; } private static void LoadConfiguration() { Dictionary previousEntrySignatures = CloneCurrentEntrySignaturesByPrefab(); if (DropNSpawnPlugin.IsSourceOfTruth) { EnsurePrimaryOverrideConfigurationFileExists(); if (ConfigurationRuntime.ReloadSourceOfTruth(EnumerateOverrideConfigurationPaths().ToList()) == DomainReloadOutcome.Loaded) { RefreshVneiCompatibility(previousEntrySignatures); } } else if (ConfigurationRuntime.ReloadSynced() == DomainReloadOutcome.Loaded) { RefreshVneiCompatibility(previousEntrySignatures, CloneCurrentEntrySignaturesByPrefab()); } } private static void ResetLoadedConfigurationState() { ClearQueuedReconcileState(); Volatile.Write(ref _synchronizedPayloadReady, value: false); Volatile.Write(ref _runtimeConfigurationSnapshot, SpawnerRuntimeConfigurationSnapshot.Empty); RuntimeState.Reset(); InvalidEntryWarnings.Clear(); SelectorCacheStore.Clear(); RuntimeStateStore.Clear(); LiveRegistryStore.ClearLocationBuckets(); ProvenanceRegistry.Clear(clearCurrentContexts: false); InvalidateTrackedSpawnerEligibility(); } private static List CloneAndNormalizeConfigurationEntries(List? configuration, string sourceName) { List list = NetworkPayloadSyncSupport.CloneEntries(Descriptor, configuration); foreach (SpawnerConfigurationEntry item in list) { item.SourcePath = (string.IsNullOrWhiteSpace(item.SourcePath) ? sourceName : item.SourcePath); NormalizeEntry(item); } return list; } private static List PrepareLocalConfigurationEntries(List? configuration, string sourceName, List warnings) { List list = CloneAndNormalizeConfigurationEntries(configuration, sourceName); List list2 = new List(); foreach (SpawnerConfigurationEntry item in list) { if (TryAcceptLocalConfigurationEntry(item, warnings)) { list2.Add(item); } } return list2; } private static bool TryAcceptLocalConfigurationEntry(SpawnerConfigurationEntry entry, List warnings) { if (!entry.Enabled) { return true; } string text = CreateConfigurationContext(entry); if (string.IsNullOrWhiteSpace(entry.Prefab)) { warnings.Add("Entry '" + text + "' is missing required prefab."); return false; } if (!TryResolveConfiguredSpawnerPrefab(entry.Prefab, out var hasSpawnerComponents)) { warnings.Add("Entry '" + text + "' references unknown spawner prefab '" + entry.Prefab + "'."); return false; } if (!hasSpawnerComponents) { warnings.Add("Entry '" + text + "' references '" + entry.Prefab + "', but it is not a SpawnArea/CreatureSpawner prefab."); return false; } return true; } private static bool TryResolveConfiguredSpawnerPrefab(string prefabName, out bool hasSpawnerComponents) { hasSpawnerComponents = true; if ((Object)(object)ZNetScene.instance == (Object)null || string.IsNullOrWhiteSpace(prefabName)) { return true; } GameObject prefab = ZNetScene.instance.GetPrefab(prefabName.Trim()); if ((Object)(object)prefab == (Object)null) { hasSpawnerComponents = false; return false; } hasSpawnerComponents = (Object)(object)prefab.GetComponentInChildren(true) != (Object)null || (Object)(object)prefab.GetComponentInChildren(true) != (Object)null; return true; } private static SyncedSpawnerConfigurationState BuildSyncedConfigurationState(List configuration, string sourceName) { using (BeginInvalidEntryWarningSuppressionForSyncedClientBuild(sourceName)) { SyncedSpawnerConfigurationState syncedSpawnerConfigurationState = new SyncedSpawnerConfigurationState(); foreach (SpawnerConfigurationEntry item in CloneAndNormalizeConfigurationEntries(configuration, sourceName)) { if (!string.IsNullOrWhiteSpace(item.Prefab)) { RemoveEffectiveConfigurationEntry(syncedSpawnerConfigurationState.Configuration, syncedSpawnerConfigurationState.ActiveEntries, syncedSpawnerConfigurationState.ActiveEntriesByPrefab, item.Prefab, item.RuleId); if (item.Enabled) { syncedSpawnerConfigurationState.Configuration.Add(item); syncedSpawnerConfigurationState.ActiveEntries.Add(item); GetOrCreateActiveEntries(syncedSpawnerConfigurationState.ActiveEntriesByPrefab, item.Prefab).Add(item); } } } RefreshConfiguredPrefabSets(syncedSpawnerConfigurationState.ActiveEntries, syncedSpawnerConfigurationState.ConfiguredSpawnAreaPrefabs, syncedSpawnerConfigurationState.ConfiguredCreatureSpawnerPrefabs, syncedSpawnerConfigurationState.RuntimeConfiguredSpawnAreaPrefabs, syncedSpawnerConfigurationState.RuntimeConfiguredCreatureSpawnerPrefabs); syncedSpawnerConfigurationState.EntrySignaturesByPrefab = BuildActiveEntrySignaturesByPrefab(syncedSpawnerConfigurationState.ActiveEntriesByPrefab); syncedSpawnerConfigurationState.ConfigurationSignature = NetworkPayloadSyncSupport.ComputeSpawnerConfigurationSignature(syncedSpawnerConfigurationState.Configuration); return syncedSpawnerConfigurationState; } } private static SpawnerRuntimeConfigurationSnapshot BuildRuntimeConfigurationSnapshot(SyncedSpawnerConfigurationState state) { SpawnerRuntimeConfigurationSnapshot spawnerRuntimeConfigurationSnapshot = new SpawnerRuntimeConfigurationSnapshot(); foreach (KeyValuePair> item in state.ActiveEntriesByPrefab) { item.Deconstruct(out var key, out var value); string key2 = key; List list = value; CompiledSpawnerPrefabPlan compiledSpawnerPrefabPlan = new CompiledSpawnerPrefabPlan(); for (int i = 0; i < list.Count; i++) { SpawnerConfigurationEntry spawnerConfigurationEntry = list[i]; SpawnerRuntimeEntry spawnerRuntimeEntry = BuildRuntimeEntry(spawnerConfigurationEntry); if (spawnerConfigurationEntry.SpawnArea != null && HasSpawnAreaOverride(spawnerConfigurationEntry.SpawnArea)) { compiledSpawnerPrefabPlan.SpawnAreaEntries.Add(spawnerRuntimeEntry); if (!HasLocationSelector(spawnerRuntimeEntry)) { compiledSpawnerPrefabPlan.HasUnscopedSpawnAreaEntries = true; } else { foreach (string location in spawnerRuntimeEntry.Locations) { compiledSpawnerPrefabPlan.SpawnAreaSelectorLocationKeys.Add(NormalizeSelectorLocationCacheKey(location)); } } if (spawnerRuntimeEntry.RuntimeReconcile) { compiledSpawnerPrefabPlan.DynamicSpawnAreaEntries.Add(spawnerRuntimeEntry); } } if (spawnerConfigurationEntry.CreatureSpawner == null || !HasCreatureSpawnerOverride(spawnerConfigurationEntry.CreatureSpawner)) { continue; } compiledSpawnerPrefabPlan.CreatureSpawnerEntries.Add(spawnerRuntimeEntry); if (!HasLocationSelector(spawnerRuntimeEntry)) { compiledSpawnerPrefabPlan.HasUnscopedCreatureSpawnerEntries = true; } else { foreach (string location2 in spawnerRuntimeEntry.Locations) { compiledSpawnerPrefabPlan.CreatureSpawnerSelectorLocationKeys.Add(NormalizeSelectorLocationCacheKey(location2)); } } if (spawnerRuntimeEntry.RuntimeReconcile) { compiledSpawnerPrefabPlan.DynamicCreatureSpawnerEntries.Add(spawnerRuntimeEntry); } } spawnerRuntimeConfigurationSnapshot.PlansByPrefab[key2] = compiledSpawnerPrefabPlan; } spawnerRuntimeConfigurationSnapshot.ConfiguredSpawnAreaPrefabs.UnionWith(state.ConfiguredSpawnAreaPrefabs); spawnerRuntimeConfigurationSnapshot.ConfiguredCreatureSpawnerPrefabs.UnionWith(state.ConfiguredCreatureSpawnerPrefabs); spawnerRuntimeConfigurationSnapshot.RuntimeConfiguredSpawnAreaPrefabs.UnionWith(state.RuntimeConfiguredSpawnAreaPrefabs); spawnerRuntimeConfigurationSnapshot.RuntimeConfiguredCreatureSpawnerPrefabs.UnionWith(state.RuntimeConfiguredCreatureSpawnerPrefabs); return spawnerRuntimeConfigurationSnapshot; } private static SpawnerRuntimeEntry BuildRuntimeEntry(SpawnerConfigurationEntry entry) { return new SpawnerRuntimeEntry { Prefab = (entry.Prefab ?? ""), RuleId = (entry.RuleId ?? ""), Locations = CloneStringList(entry.Locations), Conditions = entry.Conditions, RuntimeReconcile = ShouldRuntimeReconcile(entry), SpawnArea = entry.SpawnArea, CreatureSpawner = entry.CreatureSpawner }; } private static SpawnerRuntimeConfigurationSnapshot GetRuntimeConfigurationSnapshot() { return Volatile.Read(in _runtimeConfigurationSnapshot) ?? SpawnerRuntimeConfigurationSnapshot.Empty; } private static void CommitSyncedConfigurationState(SyncedSpawnerConfigurationState state, string payloadToken) { SpawnerRuntimeConfigurationSnapshot value = BuildRuntimeConfigurationSnapshot(state); ResetLoadedConfigurationState(); RuntimeState.Configuration = state.Configuration; RuntimeState.ActiveEntries.AddRange(state.ActiveEntries); foreach (var (key, value2) in state.ActiveEntriesByPrefab) { RuntimeState.ActiveEntriesByPrefab[key] = value2; } foreach (string configuredSpawnAreaPrefab in state.ConfiguredSpawnAreaPrefabs) { RuntimeState.ConfiguredSpawnAreaPrefabs.Add(configuredSpawnAreaPrefab); } foreach (string configuredCreatureSpawnerPrefab in state.ConfiguredCreatureSpawnerPrefabs) { RuntimeState.ConfiguredCreatureSpawnerPrefabs.Add(configuredCreatureSpawnerPrefab); } foreach (string runtimeConfiguredSpawnAreaPrefab in state.RuntimeConfiguredSpawnAreaPrefabs) { RuntimeState.RuntimeConfiguredSpawnAreaPrefabs.Add(runtimeConfiguredSpawnAreaPrefab); } foreach (string runtimeConfiguredCreatureSpawnerPrefab in state.RuntimeConfiguredCreatureSpawnerPrefabs) { RuntimeState.RuntimeConfiguredCreatureSpawnerPrefabs.Add(runtimeConfiguredCreatureSpawnerPrefab); } ReplaceEntrySignatures(RuntimeState.CurrentEntrySignaturesByPrefab, state.EntrySignaturesByPrefab); RuntimeState.ConfigurationSignature = state.ConfigurationSignature; LoadState.LastLoadedPayload = payloadToken; LoadState.LastRejectedPayload = ""; LoadState.PendingStrictPayload = ""; LoadState.LastRejectedValidationKey = ""; Volatile.Write(ref _runtimeConfigurationSnapshot, value); Volatile.Write(ref _synchronizedPayloadReady, value: true); InvalidateTrackedSpawnerEligibility(); } private static LocalLoadResult ParseLocalConfigurationDocuments(List documents) { return ConfigurationLoadSupport.ParseLocalConfigurationDocuments(documents, ParseConfiguration, PrepareLocalConfigurationEntries, FormatYamlExceptionLocation, "Spawner override YAML must start with a root list like '- prefab: ...'."); } private static void RejectLocalConfigurationPayload(string payload, IEnumerable errors) { ConfigurationDomainHost.RejectLocalConfigurationPayload(LoadState, payload, errors, "spawner"); } private static void InvalidateTrackedSpawnerEligibility() { LiveRuntimeState.InvalidateTrackedSpawnerEligibility(); } private static void RemoveEffectiveConfigurationEntry(List configuration, List activeEntries, Dictionary> activeEntriesByPrefab, string prefabName, string ruleId) { for (int num = configuration.Count - 1; num >= 0; num--) { SpawnerConfigurationEntry spawnerConfigurationEntry = configuration[num]; if (string.Equals(spawnerConfigurationEntry.Prefab, prefabName, StringComparison.OrdinalIgnoreCase) && string.Equals(spawnerConfigurationEntry.RuleId, ruleId, StringComparison.Ordinal)) { configuration.RemoveAt(num); } } for (int num2 = activeEntries.Count - 1; num2 >= 0; num2--) { SpawnerConfigurationEntry spawnerConfigurationEntry2 = activeEntries[num2]; if (string.Equals(spawnerConfigurationEntry2.Prefab, prefabName, StringComparison.OrdinalIgnoreCase) && string.Equals(spawnerConfigurationEntry2.RuleId, ruleId, StringComparison.Ordinal)) { activeEntries.RemoveAt(num2); } } if (!activeEntriesByPrefab.TryGetValue(prefabName, out List value)) { return; } for (int num3 = value.Count - 1; num3 >= 0; num3--) { if (string.Equals(value[num3].RuleId, ruleId, StringComparison.Ordinal)) { value.RemoveAt(num3); } } if (value.Count == 0) { activeEntriesByPrefab.Remove(prefabName); } } private static List GetOrCreateActiveEntries(Dictionary> activeEntriesByPrefab, string prefabName) { if (!activeEntriesByPrefab.TryGetValue(prefabName, out List value)) { value = (activeEntriesByPrefab[prefabName] = new List()); } return value; } private static void RefreshConfiguredPrefabSets(IEnumerable entries, HashSet configuredSpawnAreaPrefabs, HashSet configuredCreatureSpawnerPrefabs, HashSet runtimeConfiguredSpawnAreaPrefabs, HashSet runtimeConfiguredCreatureSpawnerPrefabs) { configuredSpawnAreaPrefabs.Clear(); configuredCreatureSpawnerPrefabs.Clear(); runtimeConfiguredSpawnAreaPrefabs.Clear(); runtimeConfiguredCreatureSpawnerPrefabs.Clear(); foreach (SpawnerConfigurationEntry entry in entries) { if (entry.SpawnArea != null && HasSpawnAreaOverride(entry.SpawnArea)) { configuredSpawnAreaPrefabs.Add(entry.Prefab); if (ShouldRuntimeReconcile(entry)) { runtimeConfiguredSpawnAreaPrefabs.Add(entry.Prefab); } } if (entry.CreatureSpawner != null && HasCreatureSpawnerOverride(entry.CreatureSpawner)) { configuredCreatureSpawnerPrefabs.Add(entry.Prefab); if (ShouldRuntimeReconcile(entry)) { runtimeConfiguredCreatureSpawnerPrefabs.Add(entry.Prefab); } } } } private static ConfigurationLoadSupport.ParsedLocalConfiguration ParseConfiguration(string yaml, string? sourcePath) { ConfigurationLoadSupport.ParsedLocalConfiguration parsedLocalConfiguration = new ConfigurationLoadSupport.ParsedLocalConfiguration(); if (string.IsNullOrWhiteSpace(yaml)) { return parsedLocalConfiguration; } checked { using StringReader input = new StringReader(yaml); YamlStream yamlStream = new YamlStream(); yamlStream.Load(input); if (yamlStream.Documents.Count == 0) { return parsedLocalConfiguration; } foreach (YamlNode child in ((yamlStream.Documents[0].RootNode as YamlSequenceNode) ?? throw new YamlException(yamlStream.Documents[0].RootNode.Start, yamlStream.Documents[0].RootNode.End, "Spawner override YAML root must be a sequence.")).Children) { if (!(child is YamlMappingNode yamlMappingNode)) { parsedLocalConfiguration.Warnings.Add("Skipped spawner YAML node at " + FormatYamlNodeLocation(sourcePath, child.Start) + ". Expected a list item object like '- prefab: Fox' but found " + DescribeYamlNode(child) + "."); continue; } try { string input2 = SerializeYamlNode(yamlMappingNode); SpawnerConfigurationEntry spawnerConfigurationEntry = Deserializer.Deserialize(input2) ?? new SpawnerConfigurationEntry(); spawnerConfigurationEntry.SourceLine = (int)yamlMappingNode.Start.Line; spawnerConfigurationEntry.SourceColumn = (int)yamlMappingNode.Start.Column; parsedLocalConfiguration.Configuration.Add(spawnerConfigurationEntry); } catch (Exception ex) { parsedLocalConfiguration.Warnings.Add("Skipped invalid spawner entry at " + FormatYamlNodeLocation(sourcePath, yamlMappingNode.Start) + ". " + FormatEntryParseFailure(ex)); } } return parsedLocalConfiguration; } } private static string SerializeYamlNode(YamlNode node) { YamlStream yamlStream = new YamlStream(new YamlDocument(node)); using StringWriter stringWriter = new StringWriter(CultureInfo.InvariantCulture); yamlStream.Save(stringWriter, assignAnchors: false); return stringWriter.ToString(); } private static string DescribeYamlNode(YamlNode node) { if (node is YamlScalarNode yamlScalarNode) { string text = yamlScalarNode.Value ?? ""; if (text.Length != 0) { return "scalar '" + text + "'"; } return "an empty scalar"; } if (node is YamlSequenceNode) { return "a nested sequence"; } if (node is YamlMappingNode) { return "a mapping"; } return "an unknown YAML node"; } private static string FormatYamlNodeLocation(string? sourcePath, Mark mark) { string text = (string.IsNullOrWhiteSpace(sourcePath) ? "inline YAML" : Path.GetFileName(sourcePath)); if (mark.Line > 0) { text = text + ":" + mark.Line.ToString(CultureInfo.InvariantCulture); } return text; } private static string FormatEntryParseFailure(Exception ex) { if (ex is YamlException ex2) { return ex2.Message; } return ex.Message; } private static void NormalizeEntry(SpawnerConfigurationEntry entry) { entry.Prefab = (entry.Prefab ?? "").Trim(); entry.Locations = NormalizeOptionalSelectorLocations(entry.Locations); NormalizeSpawnerConditions(entry.Conditions, entry.Prefab + ".conditions", allowCreatureSpawnerRuntimeOverlapKeys: true); if (entry.SpawnArea != null) { if (entry.SpawnArea.MaxTotalSpawns.HasValue) { entry.SpawnArea.MaxTotalSpawns = ClampSpawnAreaMaxTotalSpawns(entry.SpawnArea.MaxTotalSpawns.Value); } if (entry.SpawnArea.Creatures != null) { for (int i = 0; i < entry.SpawnArea.Creatures.Count; i++) { SpawnAreaSpawnDefinition spawnAreaSpawnDefinition = entry.SpawnArea.Creatures[i]; spawnAreaSpawnDefinition.Creature = (spawnAreaSpawnDefinition.Creature ?? "").Trim(); spawnAreaSpawnDefinition.Data = NormalizeOptionalString(spawnAreaSpawnDefinition.Data); spawnAreaSpawnDefinition.Fields = NormalizeOptionalStringDictionary(spawnAreaSpawnDefinition.Fields); spawnAreaSpawnDefinition.Objects = NormalizeOptionalStringList(spawnAreaSpawnDefinition.Objects); IntRangeDefinition? level = spawnAreaSpawnDefinition.Level; if (level != null && level.HasValues()) { spawnAreaSpawnDefinition.MinLevel = RangeFormatting.GetMin(spawnAreaSpawnDefinition.Level, spawnAreaSpawnDefinition.MinLevel); spawnAreaSpawnDefinition.MaxLevel = RangeFormatting.GetMax(spawnAreaSpawnDefinition.Level, spawnAreaSpawnDefinition.MinLevel, spawnAreaSpawnDefinition.MaxLevel); } spawnAreaSpawnDefinition.Faction = FactionIntegration.Normalize(spawnAreaSpawnDefinition.Faction); } } } if (entry.CreatureSpawner != null) { NormalizeCreatureSpawnerEntryConditions(entry.Conditions, entry.Prefab + ".conditions"); IntRangeDefinition? level2 = entry.CreatureSpawner.Level; if (level2 != null && level2.HasValues()) { entry.CreatureSpawner.MinLevel = RangeFormatting.GetMin(entry.CreatureSpawner.Level, entry.CreatureSpawner.MinLevel); entry.CreatureSpawner.MaxLevel = RangeFormatting.GetMax(entry.CreatureSpawner.Level, entry.CreatureSpawner.MinLevel, entry.CreatureSpawner.MaxLevel); } entry.CreatureSpawner.Data = NormalizeOptionalString(entry.CreatureSpawner.Data); entry.CreatureSpawner.Fields = NormalizeOptionalStringDictionary(entry.CreatureSpawner.Fields); entry.CreatureSpawner.Objects = NormalizeOptionalStringList(entry.CreatureSpawner.Objects); entry.CreatureSpawner.Faction = FactionIntegration.Normalize(entry.CreatureSpawner.Faction); entry.CreatureSpawner.TimeOfDay?.Normalize(); entry.CreatureSpawner.Creature = entry.CreatureSpawner.Creature?.Trim(); entry.CreatureSpawner.RequiredGlobalKey = entry.CreatureSpawner.RequiredGlobalKey?.Trim(); entry.CreatureSpawner.BlockingGlobalKey = entry.CreatureSpawner.BlockingGlobalKey?.Trim(); } entry.RuleId = NormalizeOptionalRuleId(entry.RuleId) ?? BuildRuleId(entry); } private static string BuildRuleId(SpawnerConfigurationEntry entry) { SpawnerConfigurationEntry entry2 = new SpawnerConfigurationEntry { Prefab = entry.Prefab, Enabled = true, Locations = CloneStringList(entry.Locations), Conditions = entry.Conditions, SpawnArea = entry.SpawnArea, CreatureSpawner = entry.CreatureSpawner }; return entry.Prefab + ":" + NetworkPayloadSyncSupport.ComputeSpawnerEntryIdentitySignature(entry2); } private static string? NormalizeOptionalString(string? value) { if (value == null) { return null; } string text = value.Trim(); if (text.Length != 0) { return text; } return null; } private static string? NormalizeOptionalRuleId(string? ruleId) { if (ruleId == null) { return null; } string text = ruleId.Trim(); if (text.Length != 0) { return text; } return null; } private static List? NormalizeOptionalStringList(List? values) { if (values == null) { return null; } List list = (from value in values select (value ?? "").Trim() into value where value.Length > 0 select value).ToList(); if (list.Count != 0) { return list; } return null; } private static List? NormalizeOptionalSelectorLocations(List? values) { if (values == null) { return null; } SortedSet sortedSet = new SortedSet(StringComparer.OrdinalIgnoreCase); foreach (string value in values) { string text = (value ?? "").Trim(); if (text.Length > 0) { sortedSet.Add(text); } } if (sortedSet.Count != 0) { return sortedSet.ToList(); } return null; } private static List? CloneStringList(List? values) { if (values != null) { return new List(values); } return null; } private static bool HasLocationSelector(SpawnerConfigurationEntry? entry) { return HasLocationSelector(entry?.Locations); } private static bool HasLocationSelector(SpawnerRuntimeEntry? entry) { return HasLocationSelector(entry?.Locations); } private static bool HasLocationSelector(List? locations) { return locations?.Any((string location) => !string.IsNullOrWhiteSpace(location)) ?? false; } private static bool MatchesLocationSelector(List? locations, string? resolvedLocationPrefab) { string resolved = (resolvedLocationPrefab ?? "").Trim(); if (resolved.Length > 0) { return locations?.Any((string location) => string.Equals((location ?? "").Trim(), resolved, StringComparison.OrdinalIgnoreCase)) ?? false; } return false; } private static string FormatLocationSelector(List? locations) { if (!HasLocationSelector(locations)) { return "prefab-only"; } return "locations=[" + string.Join(", ", locations) + "]"; } private static Dictionary? NormalizeOptionalStringDictionary(Dictionary? values) { if (values == null) { return null; } Dictionary dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); foreach (KeyValuePair value in values) { object obj; string text3; (obj, text3) = (KeyValuePair)(ref value); if (obj == null) { obj = ""; } string text4 = ((string)obj).Trim(); if (text4.Length != 0) { dictionary[text4] = (text3 ?? "").Trim(); } } if (dictionary.Count != 0) { return dictionary; } return null; } private static void NormalizeSpawnerConditions(ConditionsDefinition? conditions, string context, bool allowCreatureSpawnerRuntimeOverlapKeys) { ConditionDialectSupport.StripUnsupportedSpawnerTargetFields(conditions, context, allowCreatureSpawnerRuntimeOverlapKeys, WarnInvalidEntry); } private static void NormalizeCreatureSpawnerEntryConditions(ConditionsDefinition? conditions, string context) { ConditionDialectSupport.StripUnsupportedCreatureSpawnerEntryFields(conditions, context, WarnInvalidEntry); } private static IEnumerable EnumerateOverrideConfigurationPaths() { return DomainConfigurationFileSupport.EnumerateOverrideConfigurationPaths("spawner", PrimaryOverrideConfigurationPathYml, PrimaryOverrideConfigurationPathYaml); } private static bool IsOverrideConfigurationFileName(string fileName) { return DomainConfigurationFileSupport.IsOverrideConfigurationFileName("spawner", fileName); } private static void CaptureSnapshotsIfNeeded() { if (_snapshotsCaptured) { return; } foreach (GameObject item in EnumerateRootPrefabs()) { CaptureSpawnAreaSnapshots(item); CaptureCreatureSpawnerSnapshots(item); CapturedRootPrefabNames.Add(((Object)item).name); } _snapshotsCaptured = true; } private static void ResetReferenceSnapshots() { SpawnAreaSnapshots.Clear(); CreatureSpawnerSnapshots.Clear(); SpawnAreaSnapshotsByExactKey.Clear(); CreatureSpawnerSnapshotsByExactKey.Clear(); SpawnAreaSnapshotsByName.Clear(); CreatureSpawnerSnapshotsByName.Clear(); CapturedRootPrefabNames.Clear(); _snapshotsCaptured = false; } private static void ResetRuntimeState(bool preserveLiveRegistries) { ClearQueuedReconcileState(); LiveReconcilerState.Clear(); LiveRegistryStore.ClearRuntimeView(); LiveRuntimeState.ClearComponentCatalogs(); SelectorCacheStore.Clear(); RuntimeStateStore.Clear(); ProvenanceRegistry.Clear(clearCurrentContexts: true); if (!preserveLiveRegistries) { LiveRegistryStore.ClearLiveRegistries(); } else { RebuildTrackedSpawnerLocationBuckets(); } } private static void RebuildTrackedSpawnerLocationBuckets() { SpawnerRuntimeConfigurationSnapshot runtimeConfigurationSnapshot = GetRuntimeConfigurationSnapshot(); LiveRegistryStore.ForEachTrackedSpawnArea(delegate(SpawnArea spawnArea, string prefabName) { if (!((Object)(object)((Component)spawnArea).gameObject == (Object)null) && !string.IsNullOrWhiteSpace(prefabName)) { RefreshSpawnAreaLocationBucketMembership(spawnArea, runtimeConfigurationSnapshot); } }); LiveRegistryStore.ForEachTrackedCreatureSpawner(delegate(CreatureSpawner creatureSpawner, string prefabName) { if (!((Object)(object)((Component)creatureSpawner).gameObject == (Object)null) && !string.IsNullOrWhiteSpace(prefabName)) { RefreshCreatureSpawnerLocationBucketMembership(creatureSpawner, runtimeConfigurationSnapshot); } }); } private static IEnumerable EnumerateRootPrefabs() { HashSet seen = new HashSet(); if ((Object)(object)ZNetScene.instance == (Object)null) { yield break; } foreach (GameObject prefab in ZNetScene.instance.m_prefabs) { if ((Object)(object)prefab != (Object)null && !((Object)prefab).name.StartsWith("JVLmock_", StringComparison.OrdinalIgnoreCase) && seen.Add(((Object)prefab).GetInstanceID())) { yield return prefab; } } foreach (GameObject nonNetViewPrefab in ZNetScene.instance.m_nonNetViewPrefabs) { if ((Object)(object)nonNetViewPrefab != (Object)null && !((Object)nonNetViewPrefab).name.StartsWith("JVLmock_", StringComparison.OrdinalIgnoreCase) && seen.Add(((Object)nonNetViewPrefab).GetInstanceID())) { yield return nonNetViewPrefab; } } } private static void CaptureSpawnAreaSnapshots(GameObject rootPrefab) { SpawnArea[] componentsInChildren = rootPrefab.GetComponentsInChildren(true); foreach (SpawnArea val in componentsInChildren) { if (!((Object)(object)val == (Object)null) && !((Object)(object)((Component)val).gameObject == (Object)null)) { SpawnAreaComponentSnapshot spawnAreaComponentSnapshot = new SpawnAreaComponentSnapshot { Component = val, ConfigPrefabName = ((Object)((Component)val).gameObject).name, RootPrefabName = ((Object)rootPrefab).name, RelativePath = GetRelativePath(rootPrefab.transform, ((Component)val).transform), LevelUpChance = val.m_levelupChance, SpawnInterval = val.m_spawnIntervalSec, TriggerDistance = val.m_triggerDistance, SetPatrolSpawnPoint = val.m_setPatrolSpawnPoint, SpawnRadius = val.m_spawnRadius, NearRadius = val.m_nearRadius, FarRadius = val.m_farRadius, MaxNear = val.m_maxNear, MaxTotal = val.m_maxTotal, OnGroundOnly = val.m_onGroundOnly, Prefabs = CloneSpawnAreaSnapshots(val.m_prefabs) }; SpawnAreaSnapshots.Add(spawnAreaComponentSnapshot); SpawnAreaSnapshotsByExactKey[BuildExactKey(spawnAreaComponentSnapshot.RootPrefabName, spawnAreaComponentSnapshot.RelativePath, "SpawnArea")] = spawnAreaComponentSnapshot; AddSnapshotByName(SpawnAreaSnapshotsByName, spawnAreaComponentSnapshot.ConfigPrefabName, spawnAreaComponentSnapshot); } } } private static void CaptureCreatureSpawnerSnapshots(GameObject rootPrefab) { CreatureSpawner[] componentsInChildren = rootPrefab.GetComponentsInChildren(true); foreach (CreatureSpawner val in componentsInChildren) { if (!((Object)(object)val == (Object)null) && !((Object)(object)((Component)val).gameObject == (Object)null)) { CreatureSpawnerComponentSnapshot creatureSpawnerComponentSnapshot = new CreatureSpawnerComponentSnapshot { Component = val, ConfigPrefabName = ((Object)((Component)val).gameObject).name, RootPrefabName = ((Object)rootPrefab).name, RelativePath = GetRelativePath(rootPrefab.transform, ((Component)val).transform), CreaturePrefab = val.m_creaturePrefab, MinLevel = val.m_minLevel, MaxLevel = val.m_maxLevel, LevelUpChance = val.m_levelupChance, RespawnTimeMinutes = val.m_respawnTimeMinuts, TriggerDistance = val.m_triggerDistance, TriggerNoise = val.m_triggerNoise, SpawnAtNight = val.m_spawnAtNight, SpawnAtDay = val.m_spawnAtDay, RequireSpawnArea = val.m_requireSpawnArea, SpawnInPlayerBase = val.m_spawnInPlayerBase, WakeUpAnimation = val.m_wakeUpAnimation, SpawnCheckInterval = val.m_spawnInterval, RequiredGlobalKey = (val.m_requiredGlobalKey ?? ""), BlockingGlobalKey = (val.m_blockingGlobalKey ?? ""), SetPatrolSpawnPoint = val.m_setPatrolSpawnPoint, SpawnGroupId = val.m_spawnGroupID, MaxGroupSpawned = val.m_maxGroupSpawned, SpawnGroupRadius = val.m_spawnGroupRadius, SpawnerWeight = val.m_spawnerWeight }; CreatureSpawnerSnapshots.Add(creatureSpawnerComponentSnapshot); CreatureSpawnerSnapshotsByExactKey[BuildExactKey(creatureSpawnerComponentSnapshot.RootPrefabName, creatureSpawnerComponentSnapshot.RelativePath, "CreatureSpawner")] = creatureSpawnerComponentSnapshot; AddSnapshotByName(CreatureSpawnerSnapshotsByName, creatureSpawnerComponentSnapshot.ConfigPrefabName, creatureSpawnerComponentSnapshot); } } } private static List CloneSpawnAreaSnapshots(List prefabs) { List list = new List(); if (prefabs == null) { return list; } foreach (SpawnData prefab in prefabs) { list.Add(new SpawnAreaSpawnSnapshot { Prefab = prefab.m_prefab, Weight = prefab.m_weight, MinLevel = prefab.m_minLevel, MaxLevel = prefab.m_maxLevel }); } return list; } private static void ApplyIfReady(bool queueLiveReconcile = false) { if (!IsGameDataReady()) { return; } bool flag = Volatile.Read(in _synchronizedPayloadReady); if (StandardDomainApplySupport.CanApplySynchronizedDomain(flag)) { HashSet availablePrefabs = BuildCurrentSpawnerReferencePrefabKeys().ToHashSet(StringComparer.OrdinalIgnoreCase); int num = ComputeGameDataSignature(availablePrefabs); bool flag2 = ShouldApplyLocally(); Dictionary currentEntrySignatures = CloneCurrentEntrySignaturesByPrefab(); if (!StandardDomainApplySupport.IsAlreadyApplied(_lastAppliedGameDataSignature, num, _lastAppliedDomainEnabled, flag2, _lastAppliedConfigurationSignature, RuntimeState.ConfigurationSignature, _lastAppliedSynchronizedPayloadReady, flag)) { RunApplyCoordinator(availablePrefabs, num, flag2, currentEntrySignatures, queueLiveReconcile); } } } private static void ValidateConfiguredPrefabs(HashSet availablePrefabs) { foreach (var (text2, list2) in RuntimeState.ActiveEntriesByPrefab) { if (availablePrefabs.Contains(text2)) { continue; } foreach (SpawnerConfigurationEntry item in list2) { WarnInvalidEntry("Spawner prefab '" + text2 + "' from " + DescribeEntrySource(item) + " was not found among SpawnArea/CreatureSpawner prefabs."); } } } private static void RecordAppliedState(int gameDataSignature, bool domainEnabled, Dictionary currentEntrySignatures) { _lastAppliedGameDataSignature = gameDataSignature; _lastAppliedDomainEnabled = domainEnabled; _lastAppliedConfigurationSignature = RuntimeState.ConfigurationSignature; _lastAppliedSynchronizedPayloadReady = Volatile.Read(in _synchronizedPayloadReady); ReplaceEntrySignatures(_lastAppliedEntrySignaturesByPrefab, currentEntrySignatures); } private static Dictionary CloneCurrentEntrySignaturesByPrefab() { return new Dictionary(RuntimeState.CurrentEntrySignaturesByPrefab, StringComparer.OrdinalIgnoreCase); } private static HashSet BuildLastAppliedPrefabs() { HashSet hashSet = new HashSet(StringComparer.OrdinalIgnoreCase); if (_lastAppliedDomainEnabled != true) { return hashSet; } foreach (string key in _lastAppliedEntrySignaturesByPrefab.Keys) { hashSet.Add(key); } return hashSet; } private static Dictionary BuildActiveEntrySignaturesByPrefab() { return BuildActiveEntrySignaturesByPrefab(RuntimeState.ActiveEntriesByPrefab); } private static Dictionary BuildActiveEntrySignaturesByPrefab(Dictionary> activeEntriesByPrefab) { return DomainEntrySignatureSupport.BuildSignaturesByKey(activeEntriesByPrefab, NetworkPayloadSyncSupport.ComputeSpawnerConfigurationSignature); } private static HashSet BuildDirtyPrefabs(Dictionary previous, Dictionary current) { return DomainDictionaryDiffSupport.BuildDirtyKeys(previous, current); } private static HashSet BuildRegisteredCatchupPrefabs(bool domainEnabled, Dictionary currentEntrySignatures) { HashSet hashSet = new HashSet(StringComparer.OrdinalIgnoreCase); if (domainEnabled) { foreach (string key in currentEntrySignatures.Keys) { hashSet.Add(key); } } if (_lastAppliedDomainEnabled == true) { foreach (string key2 in _lastAppliedEntrySignaturesByPrefab.Keys) { hashSet.Add(key2); } } return hashSet; } private static void ReplaceEntrySignatures(Dictionary target, Dictionary source) { DomainDictionaryDiffSupport.ReplaceEntries(target, source); } private static void ReapplyRegisteredLiveObjects(bool domainEnabled, HashSet prefabs) { ReapplyRegisteredLiveObjects(domainEnabled, prefabs, GetRuntimeConfigurationSnapshot()); } private static void ReapplyOrQueueRegisteredLiveObjects(bool domainEnabled, HashSet prefabs) { ReapplyOrQueueRegisteredLiveObjects(domainEnabled, prefabs, GetRuntimeConfigurationSnapshot()); } private static bool TryGetTargetedSelectorLocationKeys(SpawnerRuntimeConfigurationSnapshot? runtimeConfigurationSnapshot, string prefabName, bool forSpawnArea, out HashSet? selectorLocationKeys) { selectorLocationKeys = null; if (runtimeConfigurationSnapshot == null || string.IsNullOrWhiteSpace(prefabName) || !runtimeConfigurationSnapshot.PlansByPrefab.TryGetValue(prefabName, out CompiledSpawnerPrefabPlan value)) { return false; } if (forSpawnArea) { if (value.HasUnscopedSpawnAreaEntries || value.SpawnAreaSelectorLocationKeys.Count == 0) { return false; } selectorLocationKeys = value.SpawnAreaSelectorLocationKeys; return true; } if (value.HasUnscopedCreatureSpawnerEntries || value.CreatureSpawnerSelectorLocationKeys.Count == 0) { return false; } selectorLocationKeys = value.CreatureSpawnerSelectorLocationKeys; return true; } private static void RestoreSpawnArea(SpawnArea target, SpawnAreaLiveSnapshot snapshot) { ClearAppliedSpawnAreaPostSpawnOverrides(target); ClearAppliedSpawnAreaTotalSpawnLimit(target); RestoreSpawnAreaValues(target, snapshot.LevelUpChance, snapshot.SpawnInterval, snapshot.TriggerDistance, snapshot.SetPatrolSpawnPoint, snapshot.SpawnRadius, snapshot.NearRadius, snapshot.FarRadius, snapshot.MaxNear, snapshot.MaxTotal, snapshot.OnGroundOnly, snapshot.Prefabs); } private static void RestoreCreatureSpawner(CreatureSpawner target, CreatureSpawnerLiveSnapshot snapshot) { LiveReconcilerState.ClearAppliedCreatureSpawnerOverrides(target); RestoreCreatureSpawnerValues(target, snapshot.CreaturePrefab, snapshot.MinLevel, snapshot.MaxLevel, snapshot.LevelUpChance, snapshot.RespawnTimeMinutes, snapshot.TriggerDistance, snapshot.TriggerNoise, snapshot.SpawnAtNight, snapshot.SpawnAtDay, snapshot.RequireSpawnArea, snapshot.SpawnInPlayerBase, snapshot.WakeUpAnimation, snapshot.SpawnCheckInterval, snapshot.RequiredGlobalKey, snapshot.BlockingGlobalKey, snapshot.SetPatrolSpawnPoint, snapshot.SpawnGroupId, snapshot.MaxGroupSpawned, snapshot.SpawnGroupRadius, snapshot.SpawnerWeight); } private static void RestoreSpawnAreaValues(SpawnArea target, float levelUpChance, float spawnInterval, float triggerDistance, bool setPatrolSpawnPoint, float spawnRadius, float nearRadius, float farRadius, int maxNear, int maxTotal, bool onGroundOnly, List prefabs) { target.m_levelupChance = levelUpChance; target.m_spawnIntervalSec = spawnInterval; target.m_triggerDistance = triggerDistance; target.m_setPatrolSpawnPoint = setPatrolSpawnPoint; target.m_spawnRadius = spawnRadius; target.m_nearRadius = nearRadius; target.m_farRadius = farRadius; target.m_maxNear = maxNear; target.m_maxTotal = maxTotal; target.m_onGroundOnly = onGroundOnly; target.m_prefabs = BuildSpawnAreaPrefabs(prefabs); } private static void RestoreCreatureSpawnerValues(CreatureSpawner target, GameObject? creaturePrefab, int minLevel, int maxLevel, float levelUpChance, float respawnTimeMinutes, float triggerDistance, float triggerNoise, bool spawnAtNight, bool spawnAtDay, bool requireSpawnArea, bool spawnInPlayerBase, bool wakeUpAnimation, int spawnCheckInterval, string requiredGlobalKey, string blockingGlobalKey, bool setPatrolSpawnPoint, int spawnGroupId, int maxGroupSpawned, float spawnGroupRadius, float spawnerWeight) { target.m_creaturePrefab = creaturePrefab; target.m_minLevel = minLevel; target.m_maxLevel = maxLevel; target.m_levelupChance = levelUpChance; target.m_respawnTimeMinuts = respawnTimeMinutes; ApplyDefaultZeroCreatureSpawnerRespawnTime(target, yamlRespawnTimeSpecified: false); target.m_triggerDistance = triggerDistance; target.m_triggerNoise = triggerNoise; target.m_spawnAtNight = spawnAtNight; target.m_spawnAtDay = spawnAtDay; target.m_requireSpawnArea = requireSpawnArea; target.m_spawnInPlayerBase = spawnInPlayerBase; target.m_wakeUpAnimation = wakeUpAnimation; target.m_spawnInterval = Math.Max(1, spawnCheckInterval); target.m_requiredGlobalKey = requiredGlobalKey; target.m_blockingGlobalKey = blockingGlobalKey; target.m_setPatrolSpawnPoint = setPatrolSpawnPoint; target.m_spawnGroupID = spawnGroupId; target.m_maxGroupSpawned = maxGroupSpawned; target.m_spawnGroupRadius = spawnGroupRadius; target.m_spawnerWeight = spawnerWeight; } private static void ApplySpawnArea(SpawnArea target, SpawnAreaDefinition definition, string context) { if (definition.LevelUpChance.HasValue) { target.m_levelupChance = Mathf.Max(0f, definition.LevelUpChance.Value); } if (definition.SpawnInterval.HasValue) { target.m_spawnIntervalSec = Mathf.Max(0f, definition.SpawnInterval.Value); } if (definition.TriggerDistance.HasValue) { target.m_triggerDistance = Mathf.Max(0f, definition.TriggerDistance.Value); } if (definition.SetPatrolSpawnPoint.HasValue) { target.m_setPatrolSpawnPoint = definition.SetPatrolSpawnPoint.Value; } if (definition.SpawnRadius.HasValue) { target.m_spawnRadius = Mathf.Max(0f, definition.SpawnRadius.Value); } if (definition.NearRadius.HasValue) { target.m_nearRadius = Mathf.Max(0f, definition.NearRadius.Value); } if (definition.FarRadius.HasValue) { target.m_farRadius = Mathf.Max(0f, definition.FarRadius.Value); } if (definition.MaxNear.HasValue) { target.m_maxNear = Math.Max(0, definition.MaxNear.Value); } if (definition.MaxTotal.HasValue) { target.m_maxTotal = Math.Max(0, definition.MaxTotal.Value); } if (definition.OnGroundOnly.HasValue) { target.m_onGroundOnly = definition.OnGroundOnly.Value; } List list = null; if (definition.Creatures != null) { list = BuildResolvedSpawnAreaPrefabs(definition.Creatures, context); target.m_prefabs = list.Select((SpawnAreaResolvedSpawnEntry entry) => entry.SpawnData).ToList(); } UpdateAppliedSpawnAreaPostSpawnOverrides(target, definition, list); } private static void ApplyCreatureSpawner(CreatureSpawner target, CreatureSpawnerDefinition definition, string context) { if (definition.Creature != null) { string text = definition.Creature.Trim(); if (text.Length > 0) { GameObject val = ResolveCreaturePrefab(text, context); if ((Object)(object)val != (Object)null) { target.m_creaturePrefab = val; } } else { WarnInvalidEntry("Entry '" + context + "' set creature to an empty value. Leave the key out to keep the original creature."); } } ExpandWorldSpawnDataPayload expandWorldSpawnDataPayload = ExpandWorldSpawnDataSupport.BuildPayload(target.m_creaturePrefab, definition.Data, definition.Fields, definition.Objects, context); if (expandWorldSpawnDataPayload != null) { LiveReconcilerState.SetAppliedCreatureSpawnerData(target, expandWorldSpawnDataPayload); } else { LiveReconcilerState.RemoveAppliedCreatureSpawnerData(target); } if (definition.MinLevel.HasValue) { target.m_minLevel = Math.Max(1, definition.MinLevel.Value); } if (definition.MaxLevel.HasValue) { target.m_maxLevel = Math.Max(target.m_minLevel, Math.Max(1, definition.MaxLevel.Value)); } if (definition.LevelUpChance.HasValue) { target.m_levelupChance = Mathf.Max(0f, definition.LevelUpChance.Value); } if (definition.RespawnTimeMinutes.HasValue) { target.m_respawnTimeMinuts = Mathf.Max(0f, definition.RespawnTimeMinutes.Value); } ApplyDefaultZeroCreatureSpawnerRespawnTime(target, definition.RespawnTimeMinutes.HasValue); if (definition.TriggerDistance.HasValue) { target.m_triggerDistance = Mathf.Max(0f, definition.TriggerDistance.Value); } if (definition.TriggerNoise.HasValue) { target.m_triggerNoise = Mathf.Max(0f, definition.TriggerNoise.Value); } TimeOfDayDefinition timeOfDay = definition.TimeOfDay; if (timeOfDay != null) { TimeOfDayFormatting.GetBroadSpawnFlags(timeOfDay, out var allowDay, out var allowNight); target.m_spawnAtDay = allowDay; target.m_spawnAtNight = allowNight; if (timeOfDay.HasValues()) { LiveReconcilerState.SetAppliedCreatureSpawnerTimeOfDay(target, timeOfDay); } else { LiveReconcilerState.RemoveAppliedCreatureSpawnerTimeOfDay(target); } } else { LiveReconcilerState.RemoveAppliedCreatureSpawnerTimeOfDay(target); } if (definition.RequireSpawnArea.HasValue) { target.m_requireSpawnArea = definition.RequireSpawnArea.Value; } if (definition.AllowInsidePlayerBase.HasValue) { target.m_spawnInPlayerBase = definition.AllowInsidePlayerBase.Value; } if (definition.WakeUpAnimation.HasValue) { target.m_wakeUpAnimation = definition.WakeUpAnimation.Value; } if (definition.SpawnCheckInterval.HasValue) { target.m_spawnInterval = Math.Max(1, definition.SpawnCheckInterval.Value); } if (definition.RequiredGlobalKey != null) { target.m_requiredGlobalKey = definition.RequiredGlobalKey; } if (definition.BlockingGlobalKey != null) { target.m_blockingGlobalKey = definition.BlockingGlobalKey; } if (definition.SetPatrolSpawnPoint.HasValue) { target.m_setPatrolSpawnPoint = definition.SetPatrolSpawnPoint.Value; } if (definition.SpawnGroupId.HasValue) { target.m_spawnGroupID = definition.SpawnGroupId.Value; } if (definition.MaxGroupSpawned.HasValue) { target.m_maxGroupSpawned = Math.Max(0, definition.MaxGroupSpawned.Value); } if (definition.SpawnGroupRadius.HasValue) { target.m_spawnGroupRadius = Mathf.Max(0f, definition.SpawnGroupRadius.Value); } if (definition.SpawnerWeight.HasValue) { target.m_spawnerWeight = Mathf.Max(0f, definition.SpawnerWeight.Value); } if (FactionIntegration.HasFaction(definition.Faction)) { LiveReconcilerState.SetAppliedCreatureSpawnerFaction(target, definition.Faction); } else { LiveReconcilerState.RemoveAppliedCreatureSpawnerFaction(target); } } private static bool HasSpawnAreaOverride(SpawnAreaDefinition? definition) { if (definition != null) { if (!definition.LevelUpChance.HasValue && !definition.SpawnInterval.HasValue && !definition.TriggerDistance.HasValue && !definition.SetPatrolSpawnPoint.HasValue && !definition.SpawnRadius.HasValue && !definition.NearRadius.HasValue && !definition.FarRadius.HasValue && !definition.MaxNear.HasValue && !definition.MaxTotal.HasValue && !definition.MaxTotalSpawns.HasValue && !definition.OnGroundOnly.HasValue) { return definition.Creatures != null; } return true; } return false; } private static bool HasCreatureSpawnerOverride(CreatureSpawnerDefinition? definition) { if (definition != null) { if (!FactionIntegration.HasFaction(definition.Faction) && definition.Data == null && definition.Fields == null && definition.Objects == null && definition.TimeOfDay == null && definition.Creature == null && !definition.MinLevel.HasValue && !definition.MaxLevel.HasValue && !definition.LevelUpChance.HasValue && !definition.RespawnTimeMinutes.HasValue && !definition.TriggerDistance.HasValue && !definition.TriggerNoise.HasValue && !definition.RequireSpawnArea.HasValue && !definition.AllowInsidePlayerBase.HasValue && !definition.WakeUpAnimation.HasValue && !definition.SpawnCheckInterval.HasValue && definition.RequiredGlobalKey == null && definition.BlockingGlobalKey == null && !definition.SetPatrolSpawnPoint.HasValue && !definition.SpawnGroupId.HasValue && !definition.MaxGroupSpawned.HasValue && !definition.SpawnGroupRadius.HasValue) { return definition.SpawnerWeight.HasValue; } return true; } return false; } private static void ApplyDefaultZeroCreatureSpawnerRespawnTime(CreatureSpawner? target, bool yamlRespawnTimeSpecified) { if (!((Object)(object)target == (Object)null || yamlRespawnTimeSpecified) && ShouldApplyLocally()) { int defaultZeroCreatureSpawnerRespawnTimeMinutes = PluginSettingsFacade.GetDefaultZeroCreatureSpawnerRespawnTimeMinutes(); if (defaultZeroCreatureSpawnerRespawnTimeMinutes > 0 && !(target.m_respawnTimeMinuts > 0f)) { target.m_respawnTimeMinuts = defaultZeroCreatureSpawnerRespawnTimeMinutes; } } } private static bool HasDynamicEntryConditions(SpawnerConfigurationEntry? entry) { if (entry != null) { return DropConditionEvaluator.HasDynamicConditions(entry.Conditions); } return false; } private static bool ShouldRuntimeReconcile(SpawnerConfigurationEntry? entry) { if (entry != null) { return HasDynamicEntryConditions(entry); } return false; } private static bool HasAnySpawnAreaSpawnFaction(List? prefabs) { return prefabs?.Any((SpawnAreaSpawnDefinition creature) => FactionIntegration.HasFaction(creature.Faction)) ?? false; } private static List BuildSpawnAreaPrefabs(List snapshots) { return ((IEnumerable)snapshots).Select((Func)((SpawnAreaSpawnSnapshot snapshot) => new SpawnData { m_prefab = snapshot.Prefab, m_weight = snapshot.Weight, m_minLevel = snapshot.MinLevel, m_maxLevel = snapshot.MaxLevel })).ToList(); } private static List BuildResolvedSpawnAreaPrefabs(List definitions, string context) { //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_008e: 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) //IL_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_00dd: Expected O, but got Unknown List list = new List(); for (int i = 0; i < definitions.Count; i++) { SpawnAreaSpawnDefinition spawnAreaSpawnDefinition = definitions[i]; string text = (spawnAreaSpawnDefinition.Creature ?? "").Trim(); if (text.Length == 0) { WarnInvalidEntry("Entry '" + context + "' contains a SpawnArea creature entry without a creature name."); continue; } GameObject val = ResolveCreaturePrefab(text, context); if (!((Object)(object)val == (Object)null)) { int num = Math.Max(1, spawnAreaSpawnDefinition.MinLevel ?? 1); list.Add(new SpawnAreaResolvedSpawnEntry { SpawnData = new SpawnData { m_prefab = val, m_weight = Mathf.Max(0f, spawnAreaSpawnDefinition.Weight ?? 1f), m_minLevel = num, m_maxLevel = Math.Max(num, spawnAreaSpawnDefinition.MaxLevel ?? num) }, Definition = spawnAreaSpawnDefinition, DataPayload = ExpandWorldSpawnDataSupport.BuildPayload(val, spawnAreaSpawnDefinition.Data, spawnAreaSpawnDefinition.Fields, spawnAreaSpawnDefinition.Objects, $"{context}.spawnArea.creatures[{i}]") }); } } return list; } private static void UpdateAppliedSpawnAreaPostSpawnOverrides(SpawnArea target, SpawnAreaDefinition definition, List? resolvedSpawnEntries) { ClearAppliedSpawnAreaPostSpawnOverrides(target); if (!HasAnySpawnAreaSpawnFaction(definition.Creatures) && !HasAnySpawnAreaSpawnData(definition.Creatures)) { return; } List source = target.m_prefabs ?? new List(); LiveReconcilerState.SetAppliedSpawnAreaPrefabs(target, source.ToList()); if (resolvedSpawnEntries == null || resolvedSpawnEntries.Count <= 0) { return; } foreach (SpawnAreaResolvedSpawnEntry resolvedSpawnEntry in resolvedSpawnEntries) { string text = FactionIntegration.Normalize(resolvedSpawnEntry.Definition.Faction); if (FactionIntegration.HasFaction(text)) { LiveReconcilerState.SetAppliedSpawnAreaFaction(resolvedSpawnEntry.SpawnData, text); } if (resolvedSpawnEntry.DataPayload != null) { LiveReconcilerState.SetAppliedSpawnAreaData(resolvedSpawnEntry.SpawnData, resolvedSpawnEntry.DataPayload); } } } private static void ClearAppliedSpawnAreaPostSpawnOverrides(SpawnArea? target) { if (!LiveReconcilerState.TryTakeAppliedSpawnAreaPrefabs(target, out List prefabs)) { return; } foreach (SpawnData item in prefabs) { LiveReconcilerState.RemoveAppliedSpawnAreaData(item); LiveReconcilerState.RemoveAppliedSpawnAreaFaction(item); } } private static bool HasAppliedSpawnAreaTrackedCustomizations(SpawnArea spawnArea) { if (LiveReconcilerState.TryGetAppliedSpawnAreaPrefabs(spawnArea, out List prefabs)) { return prefabs.Any((SpawnData prefab) => LiveReconcilerState.HasAppliedSpawnAreaData(prefab) || LiveReconcilerState.HasAppliedSpawnAreaFaction(prefab)); } return false; } private static void MaybeRefreshCreatureSpawnerSchedule(CreatureSpawner creatureSpawner, int previousInterval) { int instanceID = ((Object)creatureSpawner).GetInstanceID(); int num = (creatureSpawner.m_spawnInterval = Math.Max(1, creatureSpawner.m_spawnInterval)); bool flag = previousInterval != num; if (LiveReconcilerState.TryGetAppliedCreatureSpawnerCheckInterval(instanceID, out var interval)) { flag = flag || interval != num; } LiveReconcilerState.SetAppliedCreatureSpawnerCheckInterval(instanceID, num); if (flag) { RefreshCreatureSpawnerSchedule(creatureSpawner); } } private static void RefreshCreatureSpawnerSchedule(CreatureSpawner creatureSpawner) { ((MonoBehaviour)creatureSpawner).CancelInvoke("UpdateSpawner"); ZNetView val = default(ZNetView); if (((Behaviour)creatureSpawner).isActiveAndEnabled && ((Component)creatureSpawner).gameObject.activeInHierarchy && ((Component)creatureSpawner).TryGetComponent(ref val) && !((Object)(object)val == (Object)null) && val.GetZDO() != null) { float num = Mathf.Max(1f, (float)creatureSpawner.m_spawnInterval); ((MonoBehaviour)creatureSpawner).InvokeRepeating("UpdateSpawner", Random.Range(num / 2f, num), num); } } private static void MaybeResetCreatureSpawnerCaches(CreatureSpawner creatureSpawner, int previousGroupId, int previousMaxGroupSpawned, float previousGroupRadius, float previousSpawnerWeight) { if (previousGroupId != creatureSpawner.m_spawnGroupID || previousMaxGroupSpawned != creatureSpawner.m_maxGroupSpawned || !Mathf.Approximately(previousGroupRadius, creatureSpawner.m_spawnGroupRadius) || !Mathf.Approximately(previousSpawnerWeight, creatureSpawner.m_spawnerWeight)) { CreatureSpawnerCheckedLocationField?.SetValue(creatureSpawner, false); CreatureSpawnerLocationField?.SetValue(creatureSpawner, null); CreatureSpawnerSpawnGroupField?.SetValue(creatureSpawner, null); } } private static GameObject? ResolveCreaturePrefab(string prefabName, string context) { ZNetScene instance = ZNetScene.instance; GameObject val = ((instance != null) ? instance.GetPrefab(prefabName) : null); if ((Object)(object)val == (Object)null) { WarnInvalidEntry("Entry '" + context + "' references unknown creature prefab '" + prefabName + "'."); return null; } Character val2 = default(Character); BaseAI val3 = default(BaseAI); if (!val.TryGetComponent(ref val2) && !val.TryGetComponent(ref val3)) { WarnInvalidEntry("Entry '" + context + "' references '" + prefabName + "', but it is not a creature prefab."); return null; } return val; } private static bool TryGetExactContext(GameObject gameObject, string componentType, out string exactKey) { string rootPrefabName; return TryGetExactContext(gameObject, componentType, out exactKey, out rootPrefabName); } private static bool TryGetExactContext(GameObject gameObject, string componentType, out string exactKey, out string rootPrefabName) { exactKey = ""; rootPrefabName = ""; if ((Object)(object)gameObject == (Object)null) { return false; } Transform rootTransform = GetRootTransform(gameObject.transform); rootPrefabName = GetResolvedPrefabName(((Component)rootTransform).gameObject); if (rootPrefabName.Length == 0) { return false; } exactKey = BuildExactKey(rootPrefabName, GetRelativePath(rootTransform, gameObject.transform), componentType); return true; } private static bool TryGetLiveLocationContext(GameObject gameObject, out string locationPrefab, out string relativePath) { string sourceLabel; return TryGetLiveLocationContext(gameObject, out locationPrefab, out relativePath, out sourceLabel); } private static bool TryGetLiveLocationContext(GameObject gameObject, out string locationPrefab, out string relativePath, out string sourceLabel) { locationPrefab = ""; relativePath = ""; sourceLabel = ""; if ((Object)(object)gameObject == (Object)null) { return false; } if (TryGetRecordedLocationContext(gameObject, out locationPrefab, out relativePath)) { sourceLabel = "Provenance"; return true; } if (TryGetCurrentLocationSpawnContext(gameObject, out locationPrefab, out relativePath)) { sourceLabel = "SpawnLocationContext"; return true; } if (TryGetLiveLocationProxyContext(gameObject, out locationPrefab, out relativePath)) { sourceLabel = "LocationProxy"; return true; } if (TryGetClonedZoneLocationContext(gameObject, out locationPrefab)) { sourceLabel = "LocationZone"; relativePath = ""; return true; } if (TryGetPersistedSpawnerLocationContext(gameObject, out locationPrefab, out relativePath)) { sourceLabel = "PersistedProvenance"; return true; } if (TryGetDungeonGeneratorLocationContext(gameObject, out locationPrefab)) { sourceLabel = "DungeonGenerator"; relativePath = ""; return true; } if (TryGetDirectLocationContext(gameObject, out locationPrefab, out relativePath)) { sourceLabel = "Location"; return true; } if (TryGetStaticLocationContext(gameObject, out locationPrefab, out relativePath)) { sourceLabel = "LocationStatic"; return true; } if (TryGetZoneLocationContext(gameObject, out locationPrefab)) { sourceLabel = "LocationZone"; relativePath = ""; return true; } if (TryPromoteSpatialContextToRecordedProvenance(gameObject, out locationPrefab, out relativePath)) { sourceLabel = "LocationRadius"; return true; } return false; } private static string BuildExactKey(string rootPrefabName, string relativePath, string componentType) { return rootPrefabName + "|" + relativePath + "|" + componentType; } private 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) { break; } if (string.Equals(((Object)val).name, ((Object)transform).name, StringComparison.Ordinal)) { num++; } } return num; } private static string DescribeInstance(GameObject gameObject) { Transform rootTransform = GetRootTransform(gameObject.transform); return GetResolvedPrefabName(((Component)rootTransform).gameObject) + "/" + GetRelativePath(rootTransform, gameObject.transform); } private static string GetConfigPrefabName(GameObject gameObject, string componentType) { if (TryGetExactContext(gameObject, componentType, out string exactKey, out string rootPrefabName)) { if (componentType == "SpawnArea" && LiveRuntimeState.SpawnAreaCatalogsByExactKey.TryGetValue(exactKey, out SpawnAreaComponentCatalog value)) { return value.ConfigPrefabName; } if (componentType == "CreatureSpawner" && LiveRuntimeState.CreatureSpawnerCatalogsByExactKey.TryGetValue(exactKey, out CreatureSpawnerComponentCatalog value2)) { return value2.ConfigPrefabName; } string liveComponentPrefabName = GetLiveComponentPrefabName(gameObject); string relativePath = GetRelativePath(GetRootTransform(gameObject.transform), gameObject.transform); if (componentType == "SpawnArea") { LiveRuntimeState.SpawnAreaCatalogsByExactKey[exactKey] = new SpawnAreaComponentCatalog { ConfigPrefabName = liveComponentPrefabName, RootPrefabName = rootPrefabName, RelativePath = relativePath }; } else if (componentType == "CreatureSpawner") { LiveRuntimeState.CreatureSpawnerCatalogsByExactKey[exactKey] = new CreatureSpawnerComponentCatalog { ConfigPrefabName = liveComponentPrefabName, RootPrefabName = rootPrefabName, RelativePath = relativePath }; } return liveComponentPrefabName; } return GetLiveComponentPrefabName(gameObject); } private static string GetLiveComponentPrefabName(GameObject? gameObject) { string text = TrimCloneSuffix(((gameObject != null) ? ((Object)gameObject).name : null) ?? ""); if (text.Length > 0) { return text; } return GetResolvedPrefabName(gameObject); } private static string GetLocationReferencePrefabName(GameObject? gameObject) { string text = NormalizeLocationReferencePrefabName(GetResolvedPrefabName(gameObject)); if (text.Length > 0) { return text; } return NormalizeLocationReferencePrefabName(GetLiveComponentPrefabName(gameObject)); } private static string GetResolvedPrefabName(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; } string name = ((Object)gameObject).name; if (name.EndsWith("(Clone)", StringComparison.Ordinal)) { string text = name; int length = "(Clone)".Length; return text.Substring(0, text.Length - length).TrimEnd(); } return name; } private static string TrimCloneSuffix(string name) { if (string.IsNullOrWhiteSpace(name)) { return ""; } if (name.EndsWith("(Clone)", StringComparison.Ordinal)) { int length = "(Clone)".Length; return name.Substring(0, name.Length - length).TrimEnd(); } return name.Trim(); } private static string NormalizeLocationReferencePrefabName(string name) { string text = TrimCloneSuffix(name); if (text.Length >= 4) { if (text[text.Length - 1] == ')') { int num = text.LastIndexOf(" (", StringComparison.Ordinal); if (num <= 0) { return text; } ReadOnlySpan readOnlySpan = text.AsSpan(num + 2, text.Length - num - 3); if (readOnlySpan.Length == 0) { return text; } ReadOnlySpan readOnlySpan2 = readOnlySpan; for (int i = 0; i < readOnlySpan2.Length; i++) { if (!char.IsDigit(readOnlySpan2[i])) { return text; } } return text.Substring(0, num).TrimEnd(); } } return text; } private static void AddSnapshotByName(Dictionary> snapshotsByName, string configPrefabName, T snapshot) { if (!snapshotsByName.TryGetValue(configPrefabName, out List value)) { value = (snapshotsByName[configPrefabName] = new List()); } value.Add(snapshot); } private static bool HasAnySpawnAreaSpawnData(List? prefabs) { return prefabs?.Any((SpawnAreaSpawnDefinition prefab) => prefab.Data != null || prefab.Fields != null || prefab.Objects != null) ?? false; } private static string FormatYamlBool(bool value) { if (!value) { return "false"; } return "true"; } private static string FormatYamlFloat(float value) { return value.ToString("0.###", CultureInfo.InvariantCulture); } private static bool IsReferenceDefault(float value, float defaultValue) { return Math.Abs(value - defaultValue) < 0.0001f; } private static string FormatYamlString(string value) { if (value.Length == 0) { return "''"; } if (!char.IsWhiteSpace(value[0]) && !char.IsWhiteSpace(value[value.Length - 1]) && value.IndexOfAny(new char[17] { ':', '#', '{', '}', '[', ']', ',', '\'', '"', '&', '*', '!', '|', '>', '%', '@', '`' }) < 0 && value[0] != '-' && value[0] != '?' && !string.Equals(value, "null", StringComparison.OrdinalIgnoreCase) && !string.Equals(value, "true", StringComparison.OrdinalIgnoreCase) && !string.Equals(value, "false", StringComparison.OrdinalIgnoreCase)) { return value; } return "'" + value.Replace("'", "''") + "'"; } private static void WarnInvalidEntry(string message) { InvalidEntryWarnings.Warn(message); } private static InvalidEntryDiagnostics.SuppressionScope BeginInvalidEntryWarningSuppressionForSyncedClientBuild(string sourceName) { return InvalidEntryWarnings.BeginSuppressionForSyncedClientBuild(sourceName); } private static void LogPartiallyAcceptedLocalConfiguration(int totalEntries, int acceptedEntries, IEnumerable warnings) { int num = Math.Max(0, totalEntries - acceptedEntries); DropNSpawnPlugin.DropNSpawnLogger.LogWarning((object)("Skipped " + num.ToString(CultureInfo.InvariantCulture) + " invalid spawner entr" + ((num == 1) ? "y" : "ies") + " and kept " + acceptedEntries.ToString(CultureInfo.InvariantCulture) + " valid entr" + ((acceptedEntries == 1) ? "y" : "ies") + ".")); foreach (string item in warnings.Where((string message) => !string.IsNullOrWhiteSpace(message)).Distinct(StringComparer.OrdinalIgnoreCase)) { DropNSpawnPlugin.DropNSpawnLogger.LogWarning((object)item); } } private static void LogLocalConfigurationLoaded(int acceptedEntryCount, int loadedFileCount) { DropNSpawnPlugin.DropNSpawnLogger.LogInfo((object)$"Loaded {acceptedEntryCount} spawner configuration(s) from {loadedFileCount} override file(s)."); } private static void OnSourceOfTruthPayloadUnchanged() { if (!NetworkPayloadSyncSupport.IsPayloadCurrent(Descriptor, RuntimeState.ConfigurationSignature)) { ConfigurationDomainHost.PublishSyncedPayload(DropNSpawnPlugin.IsSourceOfTruth, Descriptor, RuntimeState.Configuration, RuntimeState.ConfigurationSignature); } } private static void LogSyncedSpawnerConfigurationLoaded(string payloadToken, int acceptedEntryCount) { DropNSpawnPlugin.DropNSpawnLogger.LogInfo((object)$"Loaded {acceptedEntryCount} synchronized spawner configuration(s) from the server."); } private static void LogSyncedSpawnerConfigurationFailure(string payloadToken, Exception ex) { DropNSpawnPlugin.DropNSpawnLogger.LogError((object)$"Failed to deserialize synchronized spawner payload DTO. {ex}"); } private static string CreateConfigurationContext(SpawnerConfigurationEntry entry) { return (string.IsNullOrWhiteSpace(entry.Prefab) ? "" : entry.Prefab) + " @ " + DescribeEntrySource(entry); } private static string DescribeEntrySource(SpawnerConfigurationEntry entry) { string text = DescribeEntrySource(entry.SourcePath); if (entry.SourceLine > 0) { text = text + ":" + entry.SourceLine.ToString(CultureInfo.InvariantCulture); } return text; } private static string DescribeEntrySource(string? sourcePath) { string text = sourcePath ?? ""; if (text.Length == 0) { return "unknown source"; } text.StartsWith("ServerSync:", StringComparison.Ordinal); return text; } private static string FormatYamlExceptionLocation(Exception ex) { if (!(ex is YamlException { Start: var start })) { return ""; } if (start.Line <= 0) { return ""; } return " at line " + start.Line.ToString(CultureInfo.InvariantCulture); } private static void RunApplyCoordinator(HashSet availablePrefabs, int gameDataSignature, bool domainEnabled, Dictionary currentEntrySignatures, bool queueLiveReconcile) { SpawnerDesiredState spawnerDesiredState = BuildSpawnerDesiredState(availablePrefabs, gameDataSignature, domainEnabled, currentEntrySignatures, queueLiveReconcile); StandardBaselineDesiredStateCoordinator.Run(spawnerDesiredState.ApplyPlan, spawnerDesiredState, SpawnerApplyOperations.Instance); } private static SpawnerDesiredState BuildSpawnerDesiredState(HashSet availablePrefabs, int gameDataSignature, bool domainEnabled, Dictionary currentEntrySignatures, bool queueLiveReconcile) { StandardDomainApplyPlan applyPlan = StandardDomainApplySupport.BuildPlan(_lastAppliedGameDataSignature, gameDataSignature, _lastAppliedDomainEnabled, domainEnabled, _lastAppliedEntrySignaturesByPrefab, currentEntrySignatures, EmptyEntrySignatures, _lastAppliedGameDataSignature == gameDataSignature && _lastAppliedDomainEnabled == true); return new SpawnerDesiredState { GameDataSignature = gameDataSignature, ApplyPlan = applyPlan, CurrentEntrySignatures = currentEntrySignatures, DomainEnabled = domainEnabled, QueueLiveReconcile = queueLiveReconcile, AvailablePrefabs = availablePrefabs, ReloadPrefabs = (applyPlan.DirtyKeys ?? BuildRegisteredCatchupPrefabs(domainEnabled, currentEntrySignatures)), RuntimeConfigurationSnapshot = GetRuntimeConfigurationSnapshot() }; } private static void PrepareSpawnerLiveBaseline(SpawnerDesiredState desiredState) { ClearRuntimeReconcileState(); } private static void ValidateSpawnerDesiredState(SpawnerDesiredState desiredState) { ValidateConfiguredPrefabs(desiredState.AvailablePrefabs); } private static void ApplySpawnerDesiredStateToLive(SpawnerDesiredState desiredState) { ApplyDesiredStateToLiveObjects(desiredState); } internal static bool TryInspectCurrentTarget(out string[] lines, out string error) { lock (Sync) { lines = Array.Empty(); error = ""; if ((Object)(object)Player.m_localPlayer == (Object)null) { error = "Player is not available."; return false; } GameObject val = ResolveCurrentInspectionTarget(); if ((Object)(object)val == (Object)null) { error = "No hovered or nearby spawner target found."; return false; } List list = BuildInspectionLines(val); if (list.Count == 0) { error = "The current target is not a SpawnArea or CreatureSpawner."; return false; } lines = list.ToArray(); return true; } } private static GameObject? ResolveCurrentInspectionTarget() { //IL_003a: 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_0053: 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_0029: 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_00ee: 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_0087: Unknown result type (might be due to invalid IL or missing references) //IL_00e8: Unknown result type (might be due to invalid IL or missing references) //IL_00ed: Unknown result type (might be due to invalid IL or missing references) Player localPlayer = Player.m_localPlayer; if (TryResolveInspectionTargetFromObject((localPlayer != null) ? ((Humanoid)localPlayer).GetHoverObject() : null, out GameObject targetObject)) { return targetObject; } Vector3 probePoint = (((Object)(object)Player.m_localPlayer != (Object)null) ? (((Component)Player.m_localPlayer).transform.position + ((Component)Player.m_localPlayer).transform.forward * 5f) : Vector3.zero); RaycastHit val = default(RaycastHit); if ((Object)(object)GameCamera.instance != (Object)null && Physics.Raycast(((Component)GameCamera.instance).transform.position, ((Component)GameCamera.instance).transform.forward, ref val, 100f)) { Collider collider = ((RaycastHit)(ref val)).collider; object sourceObject; if (!((Object)(object)((collider != null) ? collider.attachedRigidbody : null) != (Object)null)) { Collider collider2 = ((RaycastHit)(ref val)).collider; sourceObject = ((collider2 != null) ? ((Component)collider2).gameObject : null); } else { sourceObject = ((Component)((RaycastHit)(ref val)).collider.attachedRigidbody).gameObject; } if (TryResolveInspectionTargetFromObject((GameObject?)sourceObject, out targetObject)) { return targetObject; } probePoint = ((RaycastHit)(ref val)).point; } if (!TryFindNearestInspectionTarget(probePoint, out targetObject)) { return null; } return targetObject; } private static bool TryResolveInspectionTargetFromObject(GameObject? sourceObject, out GameObject? targetObject) { targetObject = null; if ((Object)(object)sourceObject == (Object)null) { return false; } SpawnArea val = sourceObject.GetComponent() ?? sourceObject.GetComponentInParent(true); CreatureSpawner val2 = sourceObject.GetComponent() ?? sourceObject.GetComponentInParent(true); if ((Object)(object)val == (Object)null && (Object)(object)val2 == (Object)null) { return false; } if ((Object)(object)val != (Object)null && (Object)(object)val2 == (Object)null) { targetObject = ((Component)val).gameObject; return true; } if ((Object)(object)val2 != (Object)null && (Object)(object)val == (Object)null) { targetObject = ((Component)val2).gameObject; return true; } int ancestorDepth = GetAncestorDepth(sourceObject.transform, ((Component)val).transform); int ancestorDepth2 = GetAncestorDepth(sourceObject.transform, ((Component)val2).transform); targetObject = ((ancestorDepth <= ancestorDepth2) ? ((Component)val).gameObject : ((Component)val2).gameObject); return true; } private static int GetAncestorDepth(Transform source, Transform ancestor) { int num = 0; Transform val = source; while ((Object)(object)val != (Object)null) { if (val == ancestor) { return num; } val = val.parent; num++; } return int.MaxValue; } private static bool TryFindNearestInspectionTarget(Vector3 probePoint, out GameObject? targetObject) { //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003a: 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) //IL_0097: 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_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) targetObject = null; float num = 64f; SpawnArea[] array = Object.FindObjectsByType((FindObjectsSortMode)0); Vector3 val2; foreach (SpawnArea val in array) { if (!((Object)(object)val == (Object)null) && !((Object)(object)((Component)val).gameObject == (Object)null)) { val2 = ((Component)val).transform.position - probePoint; float sqrMagnitude = ((Vector3)(ref val2)).sqrMagnitude; if (!(sqrMagnitude >= num)) { num = sqrMagnitude; targetObject = ((Component)val).gameObject; } } } CreatureSpawner[] array2 = Object.FindObjectsByType((FindObjectsSortMode)0); foreach (CreatureSpawner val3 in array2) { if (!((Object)(object)val3 == (Object)null) && !((Object)(object)((Component)val3).gameObject == (Object)null)) { val2 = ((Component)val3).transform.position - probePoint; float sqrMagnitude2 = ((Vector3)(ref val2)).sqrMagnitude; if (!(sqrMagnitude2 >= num)) { num = sqrMagnitude2; targetObject = ((Component)val3).gameObject; } } } return (Object)(object)targetObject != (Object)null; } private static List BuildInspectionLines(GameObject targetObject) { List list = new List(); if ((Object)(object)targetObject == (Object)null) { return list; } SpawnArea spawnArea = default(SpawnArea); if (targetObject.TryGetComponent(ref spawnArea)) { AppendSpawnAreaInspectionLines(list, spawnArea); return list; } CreatureSpawner creatureSpawner = default(CreatureSpawner); if (targetObject.TryGetComponent(ref creatureSpawner)) { AppendCreatureSpawnerInspectionLines(list, creatureSpawner); } return list; } private static void AppendSpawnAreaInspectionLines(List lines, SpawnArea spawnArea) { string configPrefabName = GetConfigPrefabName(((Component)spawnArea).gameObject, "SpawnArea"); lines.Add("Spawner Inspect: SpawnArea"); lines.Add("Object: " + configPrefabName + "@" + DescribeInstance(((Component)spawnArea).gameObject)); AppendResolvedLocationLines(lines, ((Component)spawnArea).gameObject, spawnArea, null); List value; List list = (RuntimeState.ActiveEntriesByPrefab.TryGetValue(configPrefabName, out value) ? value.Where((SpawnerConfigurationEntry entry) => entry.SpawnArea != null && HasSpawnAreaOverride(entry.SpawnArea)).ToList() : new List()); lines.Add($"Configured entries: {list.Count}"); MatchingEntryCache entryCache; string configPrefabName2; bool flag = TryGetActiveSpawnAreaEntryCache(spawnArea, out entryCache, out configPrefabName2); lines.Add($"Selector-matching entries: {(flag ? entryCache.Entries.Count : 0)}"); IReadOnlyList readOnlyList2; if (!flag) { IReadOnlyList readOnlyList = new List(); readOnlyList2 = readOnlyList; } else { readOnlyList2 = entryCache.Entries; } IReadOnlyList readOnlyList3 = readOnlyList2; if (readOnlyList3.Count > 0 && TrySelectWinningSpawnerEntry(((Component)spawnArea).gameObject, readOnlyList3, forSpawnArea: true, out SpawnerRuntimeEntry winningEntry) && winningEntry != null) { lines.Add("Winning entry: " + FormatInspectionEntrySummary(winningEntry)); } else { lines.Add("Winning entry: none"); } } private static void AppendCreatureSpawnerInspectionLines(List lines, CreatureSpawner creatureSpawner) { string configPrefabName = GetConfigPrefabName(((Component)creatureSpawner).gameObject, "CreatureSpawner"); lines.Add("Spawner Inspect: CreatureSpawner"); lines.Add("Object: " + configPrefabName + "@" + DescribeInstance(((Component)creatureSpawner).gameObject)); AppendResolvedLocationLines(lines, ((Component)creatureSpawner).gameObject, null, creatureSpawner); List value; List list = (RuntimeState.ActiveEntriesByPrefab.TryGetValue(configPrefabName, out value) ? value.Where((SpawnerConfigurationEntry entry) => entry.CreatureSpawner != null && HasCreatureSpawnerOverride(entry.CreatureSpawner)).ToList() : new List()); lines.Add($"Configured entries: {list.Count}"); MatchingEntryCache entryCache; string configPrefabName2; bool flag = TryGetActiveCreatureSpawnerEntryCache(creatureSpawner, out entryCache, out configPrefabName2); lines.Add($"Selector-matching entries: {(flag ? entryCache.Entries.Count : 0)}"); IReadOnlyList readOnlyList2; if (!flag) { IReadOnlyList readOnlyList = new List(); readOnlyList2 = readOnlyList; } else { readOnlyList2 = entryCache.Entries; } IReadOnlyList readOnlyList3 = readOnlyList2; if (readOnlyList3.Count > 0 && TrySelectWinningSpawnerEntry(((Component)creatureSpawner).gameObject, readOnlyList3, forSpawnArea: false, out SpawnerRuntimeEntry winningEntry) && winningEntry != null) { lines.Add("Winning entry: " + FormatInspectionEntrySummary(winningEntry)); } else { lines.Add("Winning entry: none"); } } private static void AppendResolvedLocationLines(List lines, GameObject gameObject, SpawnArea? spawnArea, CreatureSpawner? creatureSpawner) { if (TryGetLiveLocationContext(gameObject, out string locationPrefab, out string relativePath, out string sourceLabel)) { lines.Add("Resolved location: " + locationPrefab); lines.Add("Resolved path: " + ((relativePath.Length > 0) ? relativePath : "(unavailable)")); lines.Add("Resolution source: " + sourceLabel); } else { lines.Add("Resolved location: unavailable"); lines.Add("Resolved path: unavailable"); lines.Add("Resolution source: unavailable"); } AppendLocationFallbackDiagnostics(lines, gameObject); SpawnerLocationProvenance provenance = null; if ((Object)(object)spawnArea != (Object)null) { ProvenanceRegistry.TryGetSpawnAreaProvenance(spawnArea, out provenance); } else if ((Object)(object)creatureSpawner != (Object)null) { ProvenanceRegistry.TryGetCreatureSpawnerProvenance(creatureSpawner, out provenance); } if (provenance != null) { lines.Add("Recorded provenance: location=" + provenance.LocationPrefab + ", path=" + provenance.RelativePath); } } private static void AppendLocationFallbackDiagnostics(List lines, GameObject gameObject) { if (TryGetStaticLocationContext(gameObject, out string locationPrefab, out string _)) { lines.Add("Static location: " + locationPrefab); } if (TryGetZoneLocationContext(gameObject, out string locationPrefab2)) { lines.Add("Zone location: " + locationPrefab2); } if (TryGetSpatialLocationContext(gameObject, out string locationPrefab3)) { lines.Add("Radius location: " + locationPrefab3); } } private static string FormatInspectionEntrySummary(SpawnerRuntimeEntry entry) { if (entry == null) { return "(null)"; } string text = FormatLocationSelector(entry.Locations); if (entry.CreatureSpawner != null) { return text + ", creatureSpawner.creature=" + (entry.CreatureSpawner.Creature ?? "(null)"); } if (entry.SpawnArea?.Creatures != null) { return $"{text}, spawnArea.creatures={entry.SpawnArea.Creatures.Count}"; } return text; } internal static void HandleSpawnAreaInstanceAwake(SpawnArea? spawnArea) { lock (Sync) { TrackSpawnAreaInstanceInternal(spawnArea); if (!((Object)(object)spawnArea == (Object)null) && !((Object)(object)((Component)spawnArea).gameObject == (Object)null) && ShouldApplyLocally() && !ShouldBlockClientSpawnerUpdate() && !DropNSpawnPlugin.IsGameDataRefreshDeferred(DropNSpawnPlugin.ReloadDomain.Spawner) && IsGameDataReady()) { SpawnerRuntimeConfigurationSnapshot runtimeConfigurationSnapshot = GetRuntimeConfigurationSnapshot(); if (TryGetTrackedOrCurrentSpawnAreaEligibility(spawnArea, runtimeConfigurationSnapshot, out string configPrefabName, out bool configuredEligible, out bool _) && configuredEligible && TryGetActiveSpawnAreaEntryCache(spawnArea, runtimeConfigurationSnapshot, out MatchingEntryCache entryCache, out configPrefabName)) { ReconcileSpawnAreaInstanceInternal(spawnArea, entryCache.Entries, entryCache); } } } } internal static void HandleCreatureSpawnerInstanceAwake(CreatureSpawner? creatureSpawner) { lock (Sync) { TrackCreatureSpawnerInstanceInternal(creatureSpawner); if (!((Object)(object)creatureSpawner == (Object)null) && !((Object)(object)((Component)creatureSpawner).gameObject == (Object)null) && ShouldApplyLocally() && !ShouldBlockClientSpawnerUpdate() && !DropNSpawnPlugin.IsGameDataRefreshDeferred(DropNSpawnPlugin.ReloadDomain.Spawner) && IsGameDataReady()) { SpawnerRuntimeConfigurationSnapshot runtimeConfigurationSnapshot = GetRuntimeConfigurationSnapshot(); MatchingEntryCache entryCache; if (!TryGetTrackedOrCurrentCreatureSpawnerEligibility(creatureSpawner, runtimeConfigurationSnapshot, out string configPrefabName, out bool configuredEligible, out bool _) || !configuredEligible) { CaptureLiveCreatureSpawnerSnapshotIfNeeded(creatureSpawner); ApplyDefaultZeroCreatureSpawnerRespawnTime(creatureSpawner, yamlRespawnTimeSpecified: false); } else if (TryGetActiveCreatureSpawnerEntryCache(creatureSpawner, runtimeConfigurationSnapshot, out entryCache, out configPrefabName)) { ReconcileCreatureSpawnerInstanceInternal(creatureSpawner, entryCache.Entries, entryCache); } } } } internal static void UntrackSpawnAreaInstance(SpawnArea? spawnArea) { lock (Sync) { if (spawnArea != null) { UnregisterLiveSpawnArea(spawnArea); } } } internal static void UntrackCreatureSpawnerInstance(CreatureSpawner? creatureSpawner) { lock (Sync) { if (creatureSpawner != null) { UnregisterLiveCreatureSpawner(creatureSpawner); } } } private static void RegisterLiveSpawnArea(SpawnArea? spawnArea) { if ((Object)(object)spawnArea == (Object)null || (Object)(object)((Component)spawnArea).gameObject == (Object)null) { return; } SpawnerRuntimeConfigurationSnapshot runtimeConfigurationSnapshot = GetRuntimeConfigurationSnapshot(); if (LiveRegistryStore.TryGetTrackedPrefabName(spawnArea, out string prefabName)) { CaptureSpawnAreaProvenanceIfAvailable(spawnArea); if (!string.IsNullOrWhiteSpace(prefabName)) { RefreshSpawnAreaLocationBucketMembership(spawnArea, runtimeConfigurationSnapshot); } return; } CaptureSpawnAreaProvenanceIfAvailable(spawnArea); string configPrefabName = GetConfigPrefabName(((Component)spawnArea).gameObject, "SpawnArea"); if (configPrefabName.Length == 0) { return; } if (LiveRegistryStore.TryGetTrackedPrefabName(spawnArea, out string prefabName2)) { if (string.Equals(prefabName2, configPrefabName, StringComparison.OrdinalIgnoreCase)) { return; } UnregisterLiveSpawnArea(spawnArea); } LiveRegistryStore.TrackSpawnAreaPrefab(spawnArea, configPrefabName); RefreshSpawnAreaLocationBucketMembership(spawnArea, runtimeConfigurationSnapshot); } private static void TrackSpawnAreaInstanceInternal(SpawnArea? spawnArea) { RegisterLiveSpawnArea(spawnArea); } private static void RegisterLiveCreatureSpawner(CreatureSpawner? creatureSpawner) { if ((Object)(object)creatureSpawner == (Object)null || (Object)(object)((Component)creatureSpawner).gameObject == (Object)null) { return; } SpawnerRuntimeConfigurationSnapshot runtimeConfigurationSnapshot = GetRuntimeConfigurationSnapshot(); if (LiveRegistryStore.TryGetTrackedPrefabName(creatureSpawner, out string prefabName)) { CaptureCreatureSpawnerProvenanceIfAvailable(creatureSpawner); if (!string.IsNullOrWhiteSpace(prefabName)) { RefreshCreatureSpawnerLocationBucketMembership(creatureSpawner, runtimeConfigurationSnapshot); } return; } CaptureCreatureSpawnerProvenanceIfAvailable(creatureSpawner); string configPrefabName = GetConfigPrefabName(((Component)creatureSpawner).gameObject, "CreatureSpawner"); if (configPrefabName.Length == 0) { return; } if (LiveRegistryStore.TryGetTrackedPrefabName(creatureSpawner, out string prefabName2)) { if (string.Equals(prefabName2, configPrefabName, StringComparison.OrdinalIgnoreCase)) { return; } UnregisterLiveCreatureSpawner(creatureSpawner); } LiveRegistryStore.TrackCreatureSpawnerPrefab(creatureSpawner, configPrefabName); RefreshCreatureSpawnerLocationBucketMembership(creatureSpawner, runtimeConfigurationSnapshot); } private static void TrackCreatureSpawnerInstanceInternal(CreatureSpawner? creatureSpawner) { RegisterLiveCreatureSpawner(creatureSpawner); } private static IEnumerable GetRegisteredSpawnAreas(HashSet dirtyPrefabs, SpawnerRuntimeConfigurationSnapshot? runtimeConfigurationSnapshot) { CleanupRegisteredSpawnAreas(); List spawnAreaBuffer = new List(); foreach (string dirtyPrefab in dirtyPrefabs) { spawnAreaBuffer.Clear(); if (TryGetTargetedSelectorLocationKeys(runtimeConfigurationSnapshot, dirtyPrefab, forSpawnArea: true, out HashSet selectorLocationKeys) && TryGetRegisteredSpawnAreasFromLocationBuckets(dirtyPrefab, selectorLocationKeys, spawnAreaBuffer)) { foreach (SpawnArea item in spawnAreaBuffer) { yield return item; } continue; } LiveRegistryStore.AppendTrackedSpawnAreasForPrefab(dirtyPrefab, spawnAreaBuffer); foreach (SpawnArea item2 in spawnAreaBuffer) { yield return item2; } } } private static IEnumerable GetRegisteredCreatureSpawners(HashSet dirtyPrefabs, SpawnerRuntimeConfigurationSnapshot? runtimeConfigurationSnapshot) { CleanupRegisteredCreatureSpawners(); List creatureSpawnerBuffer = new List(); foreach (string dirtyPrefab in dirtyPrefabs) { creatureSpawnerBuffer.Clear(); if (TryGetTargetedSelectorLocationKeys(runtimeConfigurationSnapshot, dirtyPrefab, forSpawnArea: false, out HashSet selectorLocationKeys) && TryGetRegisteredCreatureSpawnersFromLocationBuckets(dirtyPrefab, selectorLocationKeys, creatureSpawnerBuffer)) { foreach (CreatureSpawner item in creatureSpawnerBuffer) { yield return item; } continue; } LiveRegistryStore.AppendTrackedCreatureSpawnersForPrefab(dirtyPrefab, creatureSpawnerBuffer); foreach (CreatureSpawner item2 in creatureSpawnerBuffer) { yield return item2; } } } private static bool TryGetTrackedOrCurrentSpawnAreaEligibility(SpawnArea? spawnArea, SpawnerRuntimeConfigurationSnapshot runtimeConfigurationSnapshot, out string configPrefabName, out bool configuredEligible, out bool runtimeEligible) { configPrefabName = ""; configuredEligible = false; runtimeEligible = false; if ((Object)(object)spawnArea == (Object)null || (Object)(object)((Component)spawnArea).gameObject == (Object)null) { return false; } if (LiveRegistryStore.TryGetTrackedState(spawnArea, out var trackedState)) { configPrefabName = trackedState.PrefabName; if (configPrefabName.Length == 0) { return false; } if (trackedState.EligibilityEpoch == LiveRuntimeState.TrackedSpawnerEligibilityEpoch) { configuredEligible = trackedState.ConfiguredEligible; runtimeEligible = trackedState.RuntimeEligible; return true; } configuredEligible = runtimeConfigurationSnapshot.ConfiguredSpawnAreaPrefabs.Contains(configPrefabName); runtimeEligible = runtimeConfigurationSnapshot.RuntimeConfiguredSpawnAreaPrefabs.Contains(configPrefabName); LiveRegistryStore.UpdateTrackedEligibility(spawnArea, configuredEligible, runtimeEligible, LiveRuntimeState.TrackedSpawnerEligibilityEpoch); return true; } configPrefabName = GetConfigPrefabName(((Component)spawnArea).gameObject, "SpawnArea"); if (configPrefabName.Length == 0) { return false; } configuredEligible = runtimeConfigurationSnapshot.ConfiguredSpawnAreaPrefabs.Contains(configPrefabName); runtimeEligible = runtimeConfigurationSnapshot.RuntimeConfiguredSpawnAreaPrefabs.Contains(configPrefabName); return true; } private static bool TryGetTrackedOrCurrentCreatureSpawnerEligibility(CreatureSpawner? creatureSpawner, SpawnerRuntimeConfigurationSnapshot runtimeConfigurationSnapshot, out string configPrefabName, out bool configuredEligible, out bool runtimeEligible) { configPrefabName = ""; configuredEligible = false; runtimeEligible = false; if ((Object)(object)creatureSpawner == (Object)null || (Object)(object)((Component)creatureSpawner).gameObject == (Object)null) { return false; } if (LiveRegistryStore.TryGetTrackedState(creatureSpawner, out var trackedState)) { configPrefabName = trackedState.PrefabName; if (configPrefabName.Length == 0) { return false; } if (trackedState.EligibilityEpoch == LiveRuntimeState.TrackedSpawnerEligibilityEpoch) { configuredEligible = trackedState.ConfiguredEligible; runtimeEligible = trackedState.RuntimeEligible; return true; } configuredEligible = runtimeConfigurationSnapshot.ConfiguredCreatureSpawnerPrefabs.Contains(configPrefabName); runtimeEligible = runtimeConfigurationSnapshot.RuntimeConfiguredCreatureSpawnerPrefabs.Contains(configPrefabName); LiveRegistryStore.UpdateTrackedEligibility(creatureSpawner, configuredEligible, runtimeEligible, LiveRuntimeState.TrackedSpawnerEligibilityEpoch); return true; } configPrefabName = GetConfigPrefabName(((Component)creatureSpawner).gameObject, "CreatureSpawner"); if (configPrefabName.Length == 0) { return false; } configuredEligible = runtimeConfigurationSnapshot.ConfiguredCreatureSpawnerPrefabs.Contains(configPrefabName); runtimeEligible = runtimeConfigurationSnapshot.RuntimeConfiguredCreatureSpawnerPrefabs.Contains(configPrefabName); return true; } private static void RefreshSpawnAreaLocationBucketMembership(SpawnArea? spawnArea, SpawnerRuntimeConfigurationSnapshot? runtimeConfigurationSnapshot = null) { if (!((Object)(object)spawnArea == (Object)null) && !((Object)(object)((Component)spawnArea).gameObject == (Object)null)) { SpawnerRuntimeConfigurationSnapshot runtimeConfigurationSnapshot2 = runtimeConfigurationSnapshot ?? GetRuntimeConfigurationSnapshot(); if (!TryGetTrackedOrCurrentSpawnAreaEligibility(spawnArea, runtimeConfigurationSnapshot2, out string configPrefabName, out bool configuredEligible, out bool _)) { RemoveSpawnAreaLocationBucket(spawnArea); } else if (!configuredEligible) { RemoveSpawnAreaLocationBucket(spawnArea); } else { UpdateSpawnAreaLocationBucket(spawnArea, configPrefabName); } } } private static void RefreshCreatureSpawnerLocationBucketMembership(CreatureSpawner? creatureSpawner, SpawnerRuntimeConfigurationSnapshot? runtimeConfigurationSnapshot = null) { if (!((Object)(object)creatureSpawner == (Object)null) && !((Object)(object)((Component)creatureSpawner).gameObject == (Object)null)) { SpawnerRuntimeConfigurationSnapshot runtimeConfigurationSnapshot2 = runtimeConfigurationSnapshot ?? GetRuntimeConfigurationSnapshot(); if (!TryGetTrackedOrCurrentCreatureSpawnerEligibility(creatureSpawner, runtimeConfigurationSnapshot2, out string configPrefabName, out bool configuredEligible, out bool _)) { RemoveCreatureSpawnerLocationBucket(creatureSpawner); } else if (!configuredEligible) { RemoveCreatureSpawnerLocationBucket(creatureSpawner); } else { UpdateCreatureSpawnerLocationBucket(creatureSpawner, configPrefabName); } } } private static bool TryGetRegisteredSpawnAreasFromLocationBuckets(string prefabName, HashSet selectorLocationKeys, List targetedSpawnAreas) { bool flag = false; foreach (string item in EnumerateTargetedLocationBucketKeys(selectorLocationKeys)) { string bucketKey = BuildPrefabLocationBucketKey(prefabName, item); flag |= LiveRegistryStore.AppendTrackedSpawnAreasForBucket(bucketKey, targetedSpawnAreas); } return flag; } private static bool TryGetRegisteredCreatureSpawnersFromLocationBuckets(string prefabName, HashSet selectorLocationKeys, List targetedCreatureSpawners) { bool flag = false; foreach (string item in EnumerateTargetedLocationBucketKeys(selectorLocationKeys)) { string bucketKey = BuildPrefabLocationBucketKey(prefabName, item); flag |= LiveRegistryStore.AppendTrackedCreatureSpawnersForBucket(bucketKey, targetedCreatureSpawners); } return flag; } private static IEnumerable EnumerateTargetedLocationBucketKeys(HashSet selectorLocationKeys) { if (selectorLocationKeys != null) { foreach (string selectorLocationKey in selectorLocationKeys) { if (!string.IsNullOrWhiteSpace(selectorLocationKey)) { yield return selectorLocationKey; } } } yield return ""; } private static void CleanupRegisteredSpawnAreas() { List list = new List(); LiveRegistryStore.CollectDeadSpawnAreas(list); if (list.Count == 0) { return; } foreach (SpawnArea item in list) { UnregisterLiveSpawnArea(item); } } private static void CleanupRegisteredCreatureSpawners() { List list = new List(); LiveRegistryStore.CollectDeadCreatureSpawners(list); if (list.Count == 0) { return; } foreach (CreatureSpawner item in list) { UnregisterLiveCreatureSpawner(item); } } private static void UnregisterLiveSpawnArea(SpawnArea spawnArea) { ClearAppliedSpawnAreaPostSpawnOverrides(spawnArea); ClearAppliedSpawnAreaTotalSpawnLimit(spawnArea); LiveReconcilerState.ResetPendingSpawnAreaAttempt(spawnArea); LiveRegistryStore.UntrackSpawnAreaPrefab(spawnArea, out string _, out int gameObjectInstanceId); RemoveSpawnAreaLocationBucket(spawnArea); LiveRegistryStore.RemoveLiveSnapshot(spawnArea); SelectorCacheStore.RemoveSpawnAreaEntryCache(spawnArea); RuntimeStateStore.RemoveRuntimeSignature(spawnArea); RuntimeStateStore.RemoveLocalRuntimeState(spawnArea); ProvenanceRegistry.RemoveSpawnAreaProvenance(spawnArea); if (gameObjectInstanceId == 0 && (Object)(object)spawnArea != (Object)null && (Object)(object)((Component)spawnArea).gameObject != (Object)null) { gameObjectInstanceId = ((Object)((Component)spawnArea).gameObject).GetInstanceID(); } SelectorCacheStore.RemoveStaticSelectorContext(gameObjectInstanceId); } private static void UnregisterLiveCreatureSpawner(CreatureSpawner creatureSpawner) { int instanceID = ((Object)creatureSpawner).GetInstanceID(); LiveReconcilerState.ClearAppliedCreatureSpawnerOverrides(creatureSpawner); LiveReconcilerState.RemoveAppliedCreatureSpawnerCheckInterval(instanceID); LiveRegistryStore.UntrackCreatureSpawnerPrefab(creatureSpawner, out string _, out int gameObjectInstanceId); RemoveCreatureSpawnerLocationBucket(creatureSpawner); LiveRegistryStore.RemoveLiveSnapshot(creatureSpawner); SelectorCacheStore.RemoveCreatureSpawnerEntryCache(creatureSpawner); RuntimeStateStore.RemoveRuntimeSignature(creatureSpawner); RuntimeStateStore.RemoveLocalRuntimeState(creatureSpawner); ProvenanceRegistry.RemoveCreatureSpawnerProvenance(creatureSpawner); if (gameObjectInstanceId == 0 && (Object)(object)creatureSpawner != (Object)null && (Object)(object)((Component)creatureSpawner).gameObject != (Object)null) { gameObjectInstanceId = ((Object)((Component)creatureSpawner).gameObject).GetInstanceID(); } SelectorCacheStore.RemoveStaticSelectorContext(gameObjectInstanceId); } private static string BuildPrefabLocationBucketKey(string prefabName, string locationKey) { return prefabName + "|" + NormalizeSelectorLocationCacheKey(locationKey); } private static void UpdateSpawnAreaLocationBucket(SpawnArea spawnArea, string prefabName) { if (!((Object)(object)spawnArea == (Object)null) && !((Object)(object)((Component)spawnArea).gameObject == (Object)null) && !string.IsNullOrWhiteSpace(prefabName)) { string nextBucketKey = BuildPrefabLocationBucketKey(prefabName, BuildSelectorLocationCacheKey(((Component)spawnArea).gameObject)); LiveRegistryStore.UpdateSpawnAreaLocationBucket(spawnArea, nextBucketKey); } } private static void UpdateCreatureSpawnerLocationBucket(CreatureSpawner creatureSpawner, string prefabName) { if (!((Object)(object)creatureSpawner == (Object)null) && !((Object)(object)((Component)creatureSpawner).gameObject == (Object)null) && !string.IsNullOrWhiteSpace(prefabName)) { string nextBucketKey = BuildPrefabLocationBucketKey(prefabName, BuildSelectorLocationCacheKey(((Component)creatureSpawner).gameObject)); LiveRegistryStore.UpdateCreatureSpawnerLocationBucket(creatureSpawner, nextBucketKey); } } private static void RemoveSpawnAreaLocationBucket(SpawnArea spawnArea) { LiveRegistryStore.RemoveSpawnAreaLocationBucket(spawnArea); } private static void RemoveCreatureSpawnerLocationBucket(CreatureSpawner creatureSpawner) { LiveRegistryStore.RemoveCreatureSpawnerLocationBucket(creatureSpawner); } internal static void ReapplyCreatureSpawnerGlobalDefaults() { lock (Sync) { if (IsGameDataReady()) { LiveRegistryStore.ForEachTrackedCreatureSpawner(delegate(CreatureSpawner creatureSpawner, string _) { ReconcileCreatureSpawnerInstanceCore(creatureSpawner); }); } } } internal static void BeginSpawnAreaSpawnAttempt(SpawnArea spawnArea) { lock (Sync) { if (!((Object)(object)spawnArea == (Object)null)) { if (!HasAppliedSpawnAreaTrackedCustomizations(spawnArea)) { LiveReconcilerState.ResetPendingSpawnAreaAttempt(spawnArea); } else { LiveReconcilerState.BeginPendingSpawnAreaAttempt(spawnArea); } } } } internal static void RecordSelectedSpawnAreaPrefab(SpawnArea spawnArea, SpawnData? spawnData) { lock (Sync) { if (!((Object)(object)spawnArea == (Object)null) && LiveReconcilerState.HasPendingSpawnAreaAttempt(spawnArea)) { LiveReconcilerState.SetPendingSpawnAreaSelection(spawnArea, spawnData); } } } internal static void RecordSpawnAreaSpawnPoint(SpawnArea spawnArea, bool succeeded, Vector3 spawnPoint) { //IL_003e: Unknown result type (might be due to invalid IL or missing references) lock (Sync) { if (!((Object)(object)spawnArea == (Object)null) && LiveReconcilerState.HasPendingSpawnAreaAttempt(spawnArea)) { if (!succeeded) { LiveReconcilerState.RemovePendingSpawnAreaSpawnPoint(spawnArea); } else { LiveReconcilerState.SetPendingSpawnAreaSpawnPoint(spawnArea, spawnPoint); } } } } private static void ReconcileSpawnAreaInstanceCore(SpawnArea? spawnArea) { TrackSpawnAreaInstanceInternal(spawnArea); if (IsGameDataReady() && !((Object)(object)spawnArea == (Object)null)) { if (ShouldApplyLocally() && TryGetActiveSpawnAreaEntryCache(spawnArea, out MatchingEntryCache entryCache, out string _)) { ReconcileSpawnAreaInstanceInternal(spawnArea, entryCache.Entries, entryCache); } else { RestoreSpawnAreaInstance(spawnArea); } } } private static void ReconcileCreatureSpawnerInstanceCore(CreatureSpawner? creatureSpawner) { TrackCreatureSpawnerInstanceInternal(creatureSpawner); if (IsGameDataReady() && !((Object)(object)creatureSpawner == (Object)null)) { if (ShouldApplyLocally() && TryGetActiveCreatureSpawnerEntryCache(creatureSpawner, out MatchingEntryCache entryCache, out string _)) { ReconcileCreatureSpawnerInstanceInternal(creatureSpawner, entryCache.Entries, entryCache); } else { RestoreCreatureSpawnerInstance(creatureSpawner, refreshRuntimeState: true); } } } private static void ReconcileSpawnAreaInstanceInternal(SpawnArea? spawnArea, IEnumerable? entries, MatchingEntryCache? entryCache = null, bool usePreselectedWinner = false, SpawnerRuntimeEntry? preselectedWinningEntry = null) { if (!((Object)(object)spawnArea == (Object)null) && !((Object)(object)((Component)spawnArea).gameObject == (Object)null)) { CaptureLiveSpawnAreaSnapshotIfNeeded(spawnArea); RestoreSpawnAreaInstance(spawnArea); SpawnerRuntimeEntry winningEntry = preselectedWinningEntry; bool flag = (usePreselectedWinner ? (winningEntry != null) : TrySelectWinningSpawnerEntry(((Component)spawnArea).gameObject, entries, forSpawnArea: true, out winningEntry)); if (flag && winningEntry?.SpawnArea != null) { ApplySpawnArea(spawnArea, winningEntry.SpawnArea, winningEntry.Prefab + "@" + DescribeInstance(((Component)spawnArea).gameObject)); } ApplySpawnAreaTotalSpawnLimit(spawnArea, (!flag) ? ((int?)null) : winningEntry?.SpawnArea?.MaxTotalSpawns); if (entryCache == null) { entryCache = (SelectorCacheStore.TryGetSpawnAreaEntryCache(spawnArea, out MatchingEntryCache entryCache2) ? entryCache2 : null); } UpdateSpawnAreaRuntimeSignature(spawnArea, entries, entryCache); } } private static void ReconcileCreatureSpawnerInstanceInternal(CreatureSpawner? creatureSpawner, IEnumerable? entries, MatchingEntryCache? entryCache = null, bool usePreselectedWinner = false, SpawnerRuntimeEntry? preselectedWinningEntry = null) { if (!((Object)(object)creatureSpawner == (Object)null) && !((Object)(object)((Component)creatureSpawner).gameObject == (Object)null)) { CaptureLiveCreatureSpawnerSnapshotIfNeeded(creatureSpawner); int previousInterval = Math.Max(1, creatureSpawner.m_spawnInterval); int spawnGroupID = creatureSpawner.m_spawnGroupID; int maxGroupSpawned = creatureSpawner.m_maxGroupSpawned; float spawnGroupRadius = creatureSpawner.m_spawnGroupRadius; float spawnerWeight = creatureSpawner.m_spawnerWeight; RestoreCreatureSpawnerInstance(creatureSpawner); SpawnerRuntimeEntry winningEntry = preselectedWinningEntry; if ((usePreselectedWinner ? (winningEntry != null) : TrySelectWinningSpawnerEntry(((Component)creatureSpawner).gameObject, entries, forSpawnArea: false, out winningEntry)) && winningEntry?.CreatureSpawner != null) { ApplyCreatureSpawner(creatureSpawner, winningEntry.CreatureSpawner, winningEntry.Prefab + "@" + DescribeInstance(((Component)creatureSpawner).gameObject)); } if (entryCache == null) { entryCache = (SelectorCacheStore.TryGetCreatureSpawnerEntryCache(creatureSpawner, out MatchingEntryCache entryCache2) ? entryCache2 : null); } UpdateCreatureSpawnerRuntimeSignature(creatureSpawner, entries, entryCache); MaybeRefreshCreatureSpawnerSchedule(creatureSpawner, previousInterval); MaybeResetCreatureSpawnerCaches(creatureSpawner, spawnGroupID, maxGroupSpawned, spawnGroupRadius, spawnerWeight); } } private static void CaptureLiveSpawnAreaSnapshotIfNeeded(SpawnArea spawnArea) { if (!((Object)(object)spawnArea == (Object)null) && !LiveRegistryStore.HasLiveSnapshot(spawnArea)) { LiveRegistryStore.CaptureLiveSnapshot(spawnArea, new SpawnAreaLiveSnapshot { LevelUpChance = spawnArea.m_levelupChance, SpawnInterval = spawnArea.m_spawnIntervalSec, TriggerDistance = spawnArea.m_triggerDistance, SetPatrolSpawnPoint = spawnArea.m_setPatrolSpawnPoint, SpawnRadius = spawnArea.m_spawnRadius, NearRadius = spawnArea.m_nearRadius, FarRadius = spawnArea.m_farRadius, MaxNear = spawnArea.m_maxNear, MaxTotal = spawnArea.m_maxTotal, OnGroundOnly = spawnArea.m_onGroundOnly, Prefabs = CloneSpawnAreaSnapshots(spawnArea.m_prefabs) }); } } private static void CaptureLiveCreatureSpawnerSnapshotIfNeeded(CreatureSpawner creatureSpawner) { if (!((Object)(object)creatureSpawner == (Object)null) && !LiveRegistryStore.HasLiveSnapshot(creatureSpawner)) { LiveRegistryStore.CaptureLiveSnapshot(creatureSpawner, new CreatureSpawnerLiveSnapshot { CreaturePrefab = creatureSpawner.m_creaturePrefab, MinLevel = creatureSpawner.m_minLevel, MaxLevel = creatureSpawner.m_maxLevel, LevelUpChance = creatureSpawner.m_levelupChance, RespawnTimeMinutes = creatureSpawner.m_respawnTimeMinuts, TriggerDistance = creatureSpawner.m_triggerDistance, TriggerNoise = creatureSpawner.m_triggerNoise, SpawnAtNight = creatureSpawner.m_spawnAtNight, SpawnAtDay = creatureSpawner.m_spawnAtDay, RequireSpawnArea = creatureSpawner.m_requireSpawnArea, SpawnInPlayerBase = creatureSpawner.m_spawnInPlayerBase, WakeUpAnimation = creatureSpawner.m_wakeUpAnimation, SpawnCheckInterval = creatureSpawner.m_spawnInterval, RequiredGlobalKey = (creatureSpawner.m_requiredGlobalKey ?? ""), BlockingGlobalKey = (creatureSpawner.m_blockingGlobalKey ?? ""), SetPatrolSpawnPoint = creatureSpawner.m_setPatrolSpawnPoint, SpawnGroupId = creatureSpawner.m_spawnGroupID, MaxGroupSpawned = creatureSpawner.m_maxGroupSpawned, SpawnGroupRadius = creatureSpawner.m_spawnGroupRadius, SpawnerWeight = creatureSpawner.m_spawnerWeight }); } } private static void RestoreSpawnAreaInstance(SpawnArea spawnArea) { RuntimeStateStore.RemoveRuntimeSignature(spawnArea); ClearAppliedSpawnAreaPostSpawnOverrides(spawnArea); ClearAppliedSpawnAreaTotalSpawnLimit(spawnArea); if (LiveRegistryStore.TryGetLiveSnapshot(spawnArea, out SpawnAreaLiveSnapshot snapshot)) { RestoreSpawnArea(spawnArea, snapshot); } } private static void RestoreCreatureSpawnerInstance(CreatureSpawner creatureSpawner, bool refreshRuntimeState = false) { int previousInterval = Math.Max(1, creatureSpawner.m_spawnInterval); int spawnGroupID = creatureSpawner.m_spawnGroupID; int maxGroupSpawned = creatureSpawner.m_maxGroupSpawned; float spawnGroupRadius = creatureSpawner.m_spawnGroupRadius; float spawnerWeight = creatureSpawner.m_spawnerWeight; LiveReconcilerState.ClearAppliedCreatureSpawnerOverrides(creatureSpawner); if (LiveRegistryStore.TryGetLiveSnapshot(creatureSpawner, out CreatureSpawnerLiveSnapshot snapshot)) { RestoreCreatureSpawner(creatureSpawner, snapshot); if (refreshRuntimeState) { RuntimeStateStore.RemoveRuntimeSignature(creatureSpawner); MaybeRefreshCreatureSpawnerSchedule(creatureSpawner, previousInterval); MaybeResetCreatureSpawnerCaches(creatureSpawner, spawnGroupID, maxGroupSpawned, spawnGroupRadius, spawnerWeight); } } } internal static void RecordSpawnedLocationProxyProvenance(LocationProxy? proxy, GameObject? spawnedRoot) { lock (Sync) { if (!((Object)(object)proxy == (Object)null) && !((Object)(object)spawnedRoot == (Object)null) && (TryGetActiveLocationSpawnContextPrefab(out string locationPrefab) || TryGetLocationProxyPrefabName(proxy, out locationPrefab))) { Transform transform = spawnedRoot.transform; RecordSpawnedLocationRootProvenance(transform, locationPrefab); QueueSpawnedLocationRootProvenanceScan(transform, locationPrefab); } } } internal static void RecordSpawnedLocationRootProvenance(GameObject? spawnedRoot) { lock (Sync) { if (!((Object)(object)spawnedRoot == (Object)null) && TryGetActiveLocationSpawnContextPrefab(out string locationPrefab)) { Transform transform = spawnedRoot.transform; RecordSpawnedLocationRootProvenance(transform, locationPrefab); QueueSpawnedLocationRootProvenanceScan(transform, locationPrefab); } } } internal static void BeginLocationSpawnContext(ZoneLocation? location) { lock (Sync) { PushLocationSpawnContext(GetLocationSpawnContextPrefabName(location)); } } internal static void BeginLocationSpawnContext(string? locationPrefab) { lock (Sync) { PushLocationSpawnContext(locationPrefab); } } internal static bool TryBeginDerivedLocationSpawnContext(Component? source) { lock (Sync) { if ((Object)(object)source == (Object)null || (Object)(object)source.gameObject == (Object)null) { return false; } if (!TryResolveLocationSpawnContext(source.gameObject, out string locationPrefab)) { return false; } PushLocationSpawnContext(locationPrefab); return true; } } internal static bool TryBeginDerivedLocationSpawnContext(DungeonGenerator? generator) { lock (Sync) { if (TryResolveDungeonGeneratorLocationContext(generator, out string locationPrefab)) { PushLocationSpawnContext(locationPrefab); return true; } } return TryBeginDerivedLocationSpawnContext((Component?)(object)generator); } internal static void EndLocationSpawnContext() { lock (Sync) { ProvenanceRegistry.PopLocationSpawnContext(); } } private static void PushLocationSpawnContext(string? locationPrefab) { ProvenanceRegistry.PushLocationSpawnContext(locationPrefab); } private static bool TryResolveLocationSpawnContext(GameObject gameObject, out string locationPrefab) { locationPrefab = ""; if ((Object)(object)gameObject == (Object)null) { return false; } if (TryGetActiveLocationSpawnContextPrefab(out locationPrefab)) { return locationPrefab.Length > 0; } if (TryFindRecordedLocationRoot(gameObject.transform, out Transform _, out locationPrefab)) { return locationPrefab.Length > 0; } if (TryGetLiveLocationProxyPrefab(gameObject, out locationPrefab)) { return locationPrefab.Length > 0; } if (TryGetClonedZoneLocationContext(gameObject, out locationPrefab)) { return true; } if (TryGetPersistedSpawnerLocationContext(gameObject, out locationPrefab, out string relativePath)) { return true; } if (TryGetDungeonGeneratorLocationContext(gameObject, out locationPrefab)) { return locationPrefab.Length > 0; } Location componentInParent = gameObject.GetComponentInParent(true); if ((Object)(object)componentInParent != (Object)null && TryGetLocationPrefabName(componentInParent, out locationPrefab)) { return locationPrefab.Length > 0; } if (TryGetStaticLocationContext(gameObject, out locationPrefab, out relativePath)) { return locationPrefab.Length > 0; } if (TryGetZoneLocationContext(gameObject, out locationPrefab)) { return locationPrefab.Length > 0; } if (TryGetSpatialLocationContext(gameObject, out locationPrefab)) { return locationPrefab.Length > 0; } return false; } internal static bool TryGetResolvedLocationNameForConditions(GameObject? gameObject, out string locationPrefab) { lock (Sync) { locationPrefab = ""; if ((Object)(object)gameObject == (Object)null) { return false; } if (TryGetRecordedLocationContext(gameObject, out locationPrefab, out string relativePath)) { return locationPrefab.Length > 0; } if (TryGetCurrentLocationSpawnContext(gameObject, out locationPrefab, out relativePath)) { return locationPrefab.Length > 0; } if (TryGetLiveLocationProxyContext(gameObject, out locationPrefab, out relativePath)) { return locationPrefab.Length > 0; } if (TryGetClonedZoneLocationContext(gameObject, out locationPrefab)) { return true; } if (TryGetPersistedSpawnerLocationContext(gameObject, out locationPrefab, out relativePath)) { return true; } if (TryGetDungeonGeneratorLocationContext(gameObject, out locationPrefab)) { return locationPrefab.Length > 0; } if (TryGetStaticLocationContext(gameObject, out locationPrefab, out relativePath)) { return locationPrefab.Length > 0; } if (TryGetZoneLocationContext(gameObject, out locationPrefab)) { return locationPrefab.Length > 0; } return false; } } internal static void UntrackLocationInstanceProvenance(Location? location) { lock (Sync) { if (!((Object)(object)location == (Object)null)) { ProvenanceRegistry.RemoveSpawnedLocationRoot(((Component)location).transform); } } } private static bool TryGetLiveLocationProxyContext(GameObject gameObject, out string locationPrefab, out string relativePath) { locationPrefab = ""; relativePath = ""; if ((Object)(object)gameObject == (Object)null) { return false; } LocationProxy componentInParent = gameObject.GetComponentInParent(true); if ((Object)(object)componentInParent == (Object)null || !TryGetLocationProxyPrefabName(componentInParent, out locationPrefab)) { return false; } Transform locationProxySpawnedRootTransform = GetLocationProxySpawnedRootTransform(((Component)componentInParent).transform, gameObject.transform); if ((Object)(object)locationProxySpawnedRootTransform == (Object)null) { return false; } relativePath = GetRelativePath(locationProxySpawnedRootTransform, gameObject.transform); return relativePath.Length > 0; } private static bool TryGetLocationProxyPrefabName(LocationProxy proxy, out string prefabName) { //IL_0089: Unknown result type (might be due to invalid IL or missing references) prefabName = ""; if ((Object)(object)proxy == (Object)null) { return false; } ZNetView component = ((Component)proxy).GetComponent(); ZDO val = ((component != null) ? component.GetZDO() : null); int num = ((val != null) ? val.GetInt(ExpandWorldDataLocationReferenceHash, 0) : 0); if (num != 0 && TryGetZoneLocationPrefabName(num, out prefabName)) { return true; } int num2 = ((val != null) ? val.GetInt(ZDOVars.s_location, 0) : 0); if (num2 != 0 && TryGetZoneLocationPrefabName(num2, out prefabName)) { return true; } Location componentInChildren = ((Component)proxy).GetComponentInChildren(true); if ((Object)(object)componentInChildren != (Object)null && TryGetLocationPrefabName(componentInChildren, out prefabName)) { return true; } return TryGetZoneLocationPrefabName(((Component)proxy).transform.position, out prefabName); } private static bool TryGetDirectLocationContext(GameObject gameObject, out string locationPrefab, out string relativePath) { locationPrefab = ""; relativePath = ""; if ((Object)(object)gameObject == (Object)null) { return false; } Location componentInParent = gameObject.GetComponentInParent(true); if ((Object)(object)componentInParent == (Object)null || !TryGetLocationPrefabName(componentInParent, out locationPrefab)) { return false; } relativePath = GetRelativePath(((Component)componentInParent).transform, gameObject.transform); return locationPrefab.Length > 0; } private static bool TryGetStaticLocationContext(GameObject gameObject, out string locationPrefab, out string relativePath) { //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) locationPrefab = ""; relativePath = ""; if ((Object)(object)gameObject == (Object)null) { return false; } Location val = Location.GetLocation(gameObject.transform.position, true); if ((Object)(object)val == (Object)null) { val = Location.GetZoneLocation(gameObject.transform.position); } if ((Object)(object)val == (Object)null || !TryGetLocationPrefabName(val, out locationPrefab)) { return false; } relativePath = TryGetRelativePathIfDescendant(((Component)val).transform, gameObject.transform); return locationPrefab.Length > 0; } private static bool TryGetRecordedLocationContext(GameObject gameObject, out string locationPrefab, out string relativePath) { locationPrefab = ""; relativePath = ""; if ((Object)(object)gameObject == (Object)null) { return false; } SpawnArea spawnArea = default(SpawnArea); if (gameObject.TryGetComponent(ref spawnArea) && ProvenanceRegistry.TryGetSpawnAreaProvenance(spawnArea, out SpawnerLocationProvenance provenance)) { locationPrefab = provenance.LocationPrefab; relativePath = provenance.RelativePath; return locationPrefab.Length > 0; } CreatureSpawner creatureSpawner = default(CreatureSpawner); if (gameObject.TryGetComponent(ref creatureSpawner) && ProvenanceRegistry.TryGetCreatureSpawnerProvenance(creatureSpawner, out SpawnerLocationProvenance provenance2)) { locationPrefab = provenance2.LocationPrefab; relativePath = provenance2.RelativePath; return locationPrefab.Length > 0; } return false; } private static bool TryGetPersistedSpawnerLocationContext(GameObject gameObject, out string locationPrefab, out string relativePath) { locationPrefab = ""; relativePath = ""; if ((Object)(object)gameObject == (Object)null) { return false; } ZNetView component = gameObject.GetComponent(); ZDO val = ((component != null) ? component.GetZDO() : null); locationPrefab = (((val != null) ? val.GetString(DropNSpawnLocationPrefabHash, "") : null) ?? "").Trim(); return locationPrefab.Length > 0; } private static bool TryGetCurrentLocationSpawnContext(GameObject gameObject, out string locationPrefab, out string relativePath) { locationPrefab = ""; relativePath = ""; if ((Object)(object)gameObject == (Object)null || !TryGetActiveLocationSpawnContextPrefab(out locationPrefab)) { return false; } SpawnArea val = default(SpawnArea); CreatureSpawner val2 = default(CreatureSpawner); if (!gameObject.TryGetComponent(ref val) && !gameObject.TryGetComponent(ref val2)) { return false; } return locationPrefab.Length > 0; } private static bool TryGetSpatialLocationContext(GameObject gameObject, out string locationPrefab) { //IL_0025: Unknown result type (might be due to invalid IL or missing references) locationPrefab = ""; if ((Object)(object)gameObject == (Object)null || (Object)(object)ZoneSystem.instance == (Object)null) { return false; } return TryGetSpatialLocationPrefabName(gameObject.transform.position, out locationPrefab); } private static bool TryGetSpatialLocationPrefabName(Vector3 position, out string locationPrefab) { //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_003c: 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_0053: 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_0079: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) locationPrefab = ""; if ((Object)(object)ZoneSystem.instance == (Object)null) { return false; } float num = float.MaxValue; bool flag = false; foreach (LocationInstance value in ZoneSystem.instance.m_locationInstances.Values) { string zoneLocationPrefabName = GetZoneLocationPrefabName(value.m_location); if (zoneLocationPrefabName.Length == 0) { continue; } float num2 = Mathf.Max(value.m_location.m_exteriorRadius, value.m_location.m_interiorRadius); if (!(num2 <= 0f)) { float num3 = Utils.DistanceXZ(value.m_position, position); if (!(num3 > num2) && (!flag || num3 < num)) { num = num3; locationPrefab = zoneLocationPrefabName; flag = true; } } } return flag; } private static bool TryGetZoneLocationContext(GameObject gameObject, out string locationPrefab) { //IL_0025: 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_002f: Unknown result type (might be due to invalid IL or missing references) //IL_003a: 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) locationPrefab = ""; if ((Object)(object)gameObject == (Object)null || (Object)(object)ZoneSystem.instance == (Object)null) { return false; } Vector2i zone = ZoneSystem.GetZone(gameObject.transform.position); if (!ZoneSystem.instance.m_locationInstances.TryGetValue(zone, out var value)) { return false; } string zoneLocationPrefabName = GetZoneLocationPrefabName(value.m_location); if (zoneLocationPrefabName.Length == 0) { return false; } locationPrefab = zoneLocationPrefabName; return true; } private static bool TryGetDungeonGeneratorLocationContext(GameObject gameObject, out string locationPrefab) { locationPrefab = ""; if ((Object)(object)gameObject == (Object)null) { return false; } return TryResolveDungeonGeneratorLocationContext(gameObject.GetComponentInParent(true), out locationPrefab); } private static bool TryResolveDungeonGeneratorLocationContext(DungeonGenerator? generator, out string locationPrefab) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0018: 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_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) locationPrefab = ""; if ((Object)(object)generator == (Object)null) { return false; } Vector3 originalPosition = generator.m_originalPosition; if (originalPosition == Vector3.zero) { return false; } if (!TryGetZoneLocationPrefabName(originalPosition, out locationPrefab)) { return TryGetSpatialLocationPrefabName(originalPosition, out locationPrefab); } return true; } private static bool TryGetClonedZoneLocationContext(GameObject gameObject, out string locationPrefab) { locationPrefab = ""; if (!TryGetZoneLocationContext(gameObject, out string locationPrefab2) || !locationPrefab2.Contains(':')) { return false; } locationPrefab = locationPrefab2; return true; } private static Transform? GetLocationProxySpawnedRootTransform(Transform proxyTransform, Transform target) { Transform val = target; while ((Object)(object)val != (Object)null && (Object)(object)val.parent != (Object)null) { if (val.parent == proxyTransform) { return val; } val = val.parent; } return null; } private static void RecordSpawnedLocationRootProvenance(Transform rootTransform, string locationPrefab) { ProvenanceRegistry.RecordSpawnedLocationRoot(rootTransform, locationPrefab); } private static void QueueSpawnedLocationRootProvenanceScan(Transform rootTransform, string locationPrefab) { ProvenanceRegistry.TryQueueRootProvenanceScan(rootTransform, locationPrefab, _reconcileQueueEpoch); } private static bool ProcessQueuedLocationRootProvenanceStep(double deadline) { PendingLocationRootProvenanceScan pendingScan; SpawnArea val2 = default(SpawnArea); CreatureSpawner val3 = default(CreatureSpawner); while (ProvenanceRegistry.TryPeekPendingRootScan(out pendingScan)) { if (pendingScan.Epoch != _reconcileQueueEpoch) { ProvenanceRegistry.DiscardPendingRootScan(pendingScan); continue; } if ((Object)(object)pendingScan.RootTransform == (Object)null || string.IsNullOrWhiteSpace(pendingScan.LocationPrefab)) { ProvenanceRegistry.DiscardPendingRootScan(pendingScan); return true; } PendingLocationRootProvenanceScan pendingLocationRootProvenanceScan = pendingScan; if (pendingLocationRootProvenanceScan.TraversalStack == null) { List obj = new List { pendingScan.RootTransform }; List list = obj; pendingLocationRootProvenanceScan.TraversalStack = obj; } int num = 0; while (pendingScan.TraversalStack.Count > 0) { if ((num & 0xF) == 0 && Time.realtimeSinceStartupAsDouble >= deadline) { return num > 0; } int index = pendingScan.TraversalStack.Count - 1; Transform val = pendingScan.TraversalStack[index]; pendingScan.TraversalStack.RemoveAt(index); num++; if ((Object)(object)val == (Object)null) { continue; } if (((Component)val).TryGetComponent(ref val2) && (Object)(object)val2 != (Object)null) { RecordSpawnAreaProvenance(val2, pendingScan.RootTransform, pendingScan.LocationPrefab); } if (((Component)val).TryGetComponent(ref val3) && (Object)(object)val3 != (Object)null) { RecordCreatureSpawnerProvenance(val3, pendingScan.RootTransform, pendingScan.LocationPrefab); } for (int num2 = val.childCount - 1; num2 >= 0; num2--) { Transform child = val.GetChild(num2); if ((Object)(object)child != (Object)null) { pendingScan.TraversalStack.Add(child); } } } ProvenanceRegistry.DiscardPendingRootScan(pendingScan); return true; } return false; } private static void CaptureSpawnAreaProvenanceIfAvailable(SpawnArea spawnArea) { if (!((Object)(object)spawnArea == (Object)null) && !ProvenanceRegistry.HasSpawnAreaProvenance(spawnArea) && TryResolveSpawnerProvenance(((Component)spawnArea).gameObject, out Transform rootTransform, out string locationPrefab, out string relativePath)) { RecordSpawnAreaProvenance(spawnArea, rootTransform, locationPrefab, relativePath); } } private static void CaptureCreatureSpawnerProvenanceIfAvailable(CreatureSpawner creatureSpawner) { if (!((Object)(object)creatureSpawner == (Object)null) && !ProvenanceRegistry.HasCreatureSpawnerProvenance(creatureSpawner) && TryResolveSpawnerProvenance(((Component)creatureSpawner).gameObject, out Transform rootTransform, out string locationPrefab, out string relativePath)) { RecordCreatureSpawnerProvenance(creatureSpawner, rootTransform, locationPrefab, relativePath); } } private static bool TryResolveSpawnerProvenance(GameObject gameObject, out Transform? rootTransform, out string locationPrefab, out string relativePath) { rootTransform = null; locationPrefab = ""; relativePath = ""; if ((Object)(object)gameObject == (Object)null) { return false; } if (TryFindRecordedLocationRoot(gameObject.transform, out rootTransform, out locationPrefab)) { relativePath = GetRelativePath(rootTransform, gameObject.transform); return locationPrefab.Length > 0; } if (TryGetActiveLocationSpawnContextPrefab(out locationPrefab)) { rootTransform = GetRootTransform(gameObject.transform); return locationPrefab.Length > 0; } if (TryGetLiveLocationProxyContext(gameObject, out locationPrefab, out relativePath)) { rootTransform = GetLocationContextRootTransform(gameObject); return relativePath.Length > 0; } if (TryGetClonedZoneLocationContext(gameObject, out locationPrefab)) { rootTransform = GetRootTransform(gameObject.transform); return locationPrefab.Length > 0; } if (TryGetPersistedSpawnerLocationContext(gameObject, out locationPrefab, out relativePath)) { rootTransform = GetRootTransform(gameObject.transform); return locationPrefab.Length > 0; } if (TryGetDungeonGeneratorLocationContext(gameObject, out locationPrefab)) { rootTransform = GetRootTransform(gameObject.transform); return locationPrefab.Length > 0; } Location componentInParent = gameObject.GetComponentInParent(true); if ((Object)(object)componentInParent != (Object)null && TryGetLocationPrefabName(componentInParent, out locationPrefab)) { rootTransform = ((Component)componentInParent).transform; relativePath = GetRelativePath(((Component)componentInParent).transform, gameObject.transform); return locationPrefab.Length > 0; } if (TryPromoteZoneContextToRecordedProvenance(gameObject, out locationPrefab, out relativePath)) { rootTransform = GetRootTransform(gameObject.transform); return locationPrefab.Length > 0; } if (TryPromoteSpatialContextToRecordedProvenance(gameObject, out locationPrefab, out relativePath)) { rootTransform = GetRootTransform(gameObject.transform); return locationPrefab.Length > 0; } return false; } private static bool TryFindRecordedLocationRoot(Transform transform, out Transform? rootTransform, out string locationPrefab) { return ProvenanceRegistry.TryFindRecordedLocationRoot(transform, out rootTransform, out locationPrefab); } private static Transform? GetLocationContextRootTransform(GameObject gameObject) { if ((Object)(object)gameObject == (Object)null) { return null; } LocationProxy componentInParent = gameObject.GetComponentInParent(true); if ((Object)(object)componentInParent != (Object)null) { return GetLocationProxySpawnedRootTransform(((Component)componentInParent).transform, gameObject.transform); } Location componentInParent2 = gameObject.GetComponentInParent(true); if (componentInParent2 == null) { return null; } return ((Component)componentInParent2).transform; } private static int AllocateLocationProvenanceEpoch() { return ProvenanceRegistry.AllocateLocationProvenanceEpoch(); } private static void RecordSpawnAreaProvenance(SpawnArea? spawnArea, Transform? rootTransform, string locationPrefab, string? relativePath = null) { if (!((Object)(object)spawnArea == (Object)null) && !string.IsNullOrWhiteSpace(locationPrefab)) { string relativePath2 = relativePath ?? (((Object)(object)rootTransform != (Object)null) ? GetRelativePath(rootTransform, ((Component)spawnArea).transform) : ""); ProvenanceRegistry.RecordSpawnAreaProvenance(spawnArea, new SpawnerLocationProvenance { Epoch = AllocateLocationProvenanceEpoch(), LocationPrefab = locationPrefab, RelativePath = relativePath2 }); StorePersistedSpawnerLocationContext((Component?)(object)spawnArea, locationPrefab); SelectorCacheStore.RemoveSpawnAreaEntryCache(spawnArea); if (LiveRegistryStore.TryGetTrackedPrefabName(spawnArea, out string _)) { RefreshSpawnAreaLocationBucketMembership(spawnArea); } } } private static void RecordCreatureSpawnerProvenance(CreatureSpawner? creatureSpawner, Transform? rootTransform, string locationPrefab, string? relativePath = null) { if (!((Object)(object)creatureSpawner == (Object)null) && !string.IsNullOrWhiteSpace(locationPrefab)) { string relativePath2 = relativePath ?? (((Object)(object)rootTransform != (Object)null) ? GetRelativePath(rootTransform, ((Component)creatureSpawner).transform) : ""); ProvenanceRegistry.RecordCreatureSpawnerProvenance(creatureSpawner, new SpawnerLocationProvenance { Epoch = AllocateLocationProvenanceEpoch(), LocationPrefab = locationPrefab, RelativePath = relativePath2 }); StorePersistedSpawnerLocationContext((Component?)(object)creatureSpawner, locationPrefab); SelectorCacheStore.RemoveCreatureSpawnerEntryCache(creatureSpawner); if (LiveRegistryStore.TryGetTrackedPrefabName(creatureSpawner, out string _)) { RefreshCreatureSpawnerLocationBucketMembership(creatureSpawner); } } } private static void StorePersistedSpawnerLocationContext(Component? component, string locationPrefab) { if (!((Object)(object)component == (Object)null) && !string.IsNullOrWhiteSpace(locationPrefab) && !((Object)(object)ZNet.instance == (Object)null) && ZNet.instance.IsServer()) { ZNetView component2 = component.GetComponent(); ZDO obj = ((component2 != null) ? component2.GetZDO() : null); if (obj != null) { obj.Set(DropNSpawnLocationPrefabHash, locationPrefab); } } } private static bool TryGetActiveLocationSpawnContextPrefab(out string locationPrefab) { return ProvenanceRegistry.TryGetActiveLocationSpawnContextPrefab(out locationPrefab); } private static bool TryPromoteSpatialContextToRecordedProvenance(GameObject gameObject, out string locationPrefab, out string relativePath) { locationPrefab = ""; relativePath = ""; if ((Object)(object)gameObject == (Object)null || !TryGetSpatialLocationContext(gameObject, out locationPrefab)) { return false; } SpawnArea spawnArea = default(SpawnArea); if (gameObject.TryGetComponent(ref spawnArea)) { RecordSpawnAreaProvenance(spawnArea, GetRootTransform(gameObject.transform), locationPrefab, relativePath); return true; } CreatureSpawner creatureSpawner = default(CreatureSpawner); if (gameObject.TryGetComponent(ref creatureSpawner)) { RecordCreatureSpawnerProvenance(creatureSpawner, GetRootTransform(gameObject.transform), locationPrefab, relativePath); return true; } return false; } private static bool TryPromoteZoneContextToRecordedProvenance(GameObject gameObject, out string locationPrefab, out string relativePath) { locationPrefab = ""; relativePath = ""; if ((Object)(object)gameObject == (Object)null || !TryGetZoneLocationContext(gameObject, out locationPrefab)) { return false; } SpawnArea spawnArea = default(SpawnArea); if (gameObject.TryGetComponent(ref spawnArea)) { RecordSpawnAreaProvenance(spawnArea, GetRootTransform(gameObject.transform), locationPrefab, relativePath); return true; } CreatureSpawner creatureSpawner = default(CreatureSpawner); if (gameObject.TryGetComponent(ref creatureSpawner)) { RecordCreatureSpawnerProvenance(creatureSpawner, GetRootTransform(gameObject.transform), locationPrefab, relativePath); return true; } return false; } private static bool TryGetLocationPrefabName(Location location, out string prefabName) { //IL_0018: Unknown result type (might be due to invalid IL or missing references) prefabName = ""; if ((Object)(object)location == (Object)null) { return false; } if (TryGetZoneLocationPrefabName(((Component)location).transform.position, out prefabName)) { return true; } prefabName = TrimCloneSuffix(((Object)((Component)location).gameObject).name).Trim(); return prefabName.Length > 0; } private static string GetZoneLocationPrefabName(ZoneLocation? location) { return (location?.m_prefabName ?? location?.m_prefab.Name ?? "").Trim(); } private static string GetLocationSpawnContextPrefabName(ZoneLocation? location) { string zoneLocationPrefabName = GetZoneLocationPrefabName(location); if (!zoneLocationPrefabName.Contains(':') && TryGetExpandWorldDataCurrentLocationPrefabName(out string locationPrefab) && locationPrefab.Contains(':') && string.Equals(GetExpandWorldDataBaseLocationName(locationPrefab), zoneLocationPrefabName, StringComparison.OrdinalIgnoreCase)) { return locationPrefab; } return zoneLocationPrefabName; } private static bool TryGetExpandWorldDataCurrentLocationPrefabName(out string locationPrefab) { locationPrefab = ""; if (!_expandWorldDataCurrentLocationFieldResolved) { _expandWorldDataCurrentLocationField = SafeTypeLookup.FindLoadedType("ExpandWorldData.LocationSpawning", "ExpandWorldData")?.GetField("CurrentLocation", BindingFlags.Static | BindingFlags.Public); _expandWorldDataCurrentLocationFieldResolved = true; } try { object? obj = _expandWorldDataCurrentLocationField?.GetValue(null); ZoneLocation val = (ZoneLocation)((obj is ZoneLocation) ? obj : null); if (val == null) { return false; } locationPrefab = GetZoneLocationPrefabName(val); return locationPrefab.Length > 0; } catch { return false; } } private static bool TryGetZoneLocationPrefabName(int locationHash, out string prefabName) { prefabName = ""; if (locationHash == 0 || (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) == locationHash) { prefabName = zoneLocationPrefabName; return true; } } return false; } private static bool TryGetZoneLocationPrefabName(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_0033: 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_0068: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_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) 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; } float num = Mathf.Max(value.m_location.m_exteriorRadius, value.m_location.m_interiorRadius); if (num > 0f && Utils.DistanceXZ(value.m_position, position) > num) { return false; } string zoneLocationPrefabName = GetZoneLocationPrefabName(value.m_location); if (zoneLocationPrefabName.Length == 0) { return false; } prefabName = zoneLocationPrefabName; return true; } private static string GetExpandWorldDataBaseLocationName(string? locationPrefab) { string text = (locationPrefab ?? "").Trim(); int num = text.IndexOf(':'); if (num <= 0) { return text; } return text.Substring(0, num).Trim(); } private static string TryGetRelativePathIfDescendant(Transform root, Transform target) { if ((Object)(object)root == (Object)null || (Object)(object)target == (Object)null) { return ""; } if (root == target || target.IsChildOf(root)) { return GetRelativePath(root, target); } return ""; } private static Transform GetRootTransform(Transform transform) { Transform val = transform; while ((Object)(object)val.parent != (Object)null) { val = val.parent; } return val; } internal static void QueueSpawnAreaReconcile(SpawnArea? spawnArea) { lock (Sync) { if (!((Object)(object)spawnArea == (Object)null) && !((Object)(object)((Component)spawnArea).gameObject == (Object)null)) { ReconcileQueue.TryQueue(spawnArea, _reconcileQueueEpoch); } } } internal static void QueueCreatureSpawnerReconcile(CreatureSpawner? creatureSpawner) { lock (Sync) { if (!((Object)(object)creatureSpawner == (Object)null) && !((Object)(object)((Component)creatureSpawner).gameObject == (Object)null)) { ReconcileQueue.TryQueue(creatureSpawner, _reconcileQueueEpoch); } } } internal static bool HasPendingReconcileWork() { lock (Sync) { return HasPendingReconcileWorkLocked(); } } private static bool HasPendingReconcileWorkLocked() { if (!ProvenanceRegistry.HasPendingRootScans()) { return ReconcileQueue.HasPendingWork(); } return true; } internal static bool ProcessQueuedReconcileStep(double deadline) { lock (Sync) { return TryProcessQueuedReconcileWorkLocked(deadline); } } private static bool TryProcessQueuedReconcileWorkLocked(double deadline) { if (Time.realtimeSinceStartupAsDouble >= deadline) { return false; } if (ShouldBlockClientSpawnerUpdate() || !IsGameDataReady() || DropNSpawnPlugin.IsGameDataRefreshDeferred(DropNSpawnPlugin.ReloadDomain.Spawner)) { return false; } if (ProcessQueuedLocationRootProvenanceStep(deadline)) { return true; } if (ReconcileQueue.TryDequeueNextSpawnArea(_reconcileQueueEpoch, out SpawnArea spawnArea)) { ReconcileSpawnAreaInstanceCore(spawnArea); return true; } if (ReconcileQueue.TryDequeueNextCreatureSpawner(_reconcileQueueEpoch, out CreatureSpawner creatureSpawner)) { ReconcileCreatureSpawnerInstanceCore(creatureSpawner); return true; } return false; } private static void ClearQueuedReconcileState() { _reconcileQueueEpoch++; ProvenanceRegistry.ClearPendingRootScans(); ReconcileQueue.Clear(); } private static List BuildTemplateAggregates() { Dictionary dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); Dictionary> locationPrefabsBySpawnerPrefab = BuildLocationReferenceLookup(); foreach (SpawnAreaComponentSnapshot spawnAreaSnapshot in SpawnAreaSnapshots) { if (!dictionary.TryGetValue(spawnAreaSnapshot.ConfigPrefabName, out var value)) { value = new TemplateAggregate { Prefab = spawnAreaSnapshot.ConfigPrefabName }; dictionary[spawnAreaSnapshot.ConfigPrefabName] = value; } TemplateAggregate templateAggregate = value; if (templateAggregate.SpawnArea == null) { SpawnAreaComponentSnapshot spawnAreaComponentSnapshot = (templateAggregate.SpawnArea = spawnAreaSnapshot); } TrackAggregateRootPrefab(value, spawnAreaSnapshot.RootPrefabName); TrackAggregateLocationPrefabs(value, locationPrefabsBySpawnerPrefab, spawnAreaSnapshot.ConfigPrefabName); } foreach (CreatureSpawnerComponentSnapshot creatureSpawnerSnapshot in CreatureSpawnerSnapshots) { if (!dictionary.TryGetValue(creatureSpawnerSnapshot.ConfigPrefabName, out var value2)) { value2 = new TemplateAggregate { Prefab = creatureSpawnerSnapshot.ConfigPrefabName }; dictionary[creatureSpawnerSnapshot.ConfigPrefabName] = value2; } TemplateAggregate templateAggregate = value2; if (templateAggregate.CreatureSpawner == null) { CreatureSpawnerComponentSnapshot creatureSpawnerComponentSnapshot = (templateAggregate.CreatureSpawner = creatureSpawnerSnapshot); } TrackAggregateRootPrefab(value2, creatureSpawnerSnapshot.RootPrefabName); TrackAggregateLocationPrefabs(value2, locationPrefabsBySpawnerPrefab, creatureSpawnerSnapshot.ConfigPrefabName); } List list = dictionary.Values.ToList(); foreach (TemplateAggregate item in list) { item.OwnerName = ResolveSpawnerOwnerName(item.Prefab, item.LocationPrefabs, item.RootPrefabs); } list.Sort(CompareSpawnerAggregatesForOutput); return list; } private static List> BuildTemplateAggregateSections() { List> list = PrefabOutputSections.BuildSections(BuildTemplateAggregates(), (TemplateAggregate aggregate) => aggregate.Prefab, (TemplateAggregate aggregate) => aggregate.OwnerName); foreach (PrefabOwnerSection item in list) { item.Entries.Sort(CompareSpawnerAggregatesForOutput); } return list; } private static List> BuildConfigurationTemplate() { return (from section in BuildTemplateAggregateSections() select new PrefabOwnerSection(section.OwnerName, section.Entries.Select(BuildConfigurationEntry).ToList())).ToList(); } private static string BuildReferenceConfigurationTemplate() { return PrefabOutputSections.SerializeReferenceSections((from section in BuildTemplateAggregateSections() select new PrefabOwnerSection(section.OwnerName, section.Entries.Select((TemplateAggregate aggregate) => new SpawnerReferenceEntry { Prefab = aggregate.Prefab, SpawnArea = ((aggregate.SpawnArea != null) ? ConvertSpawnArea(aggregate.SpawnArea) : null), CreatureSpawner = ((aggregate.CreatureSpawner != null) ? ConvertCreatureSpawner(aggregate.CreatureSpawner) : null) }).ToList())).ToList(), Serializer); } private static string BuildLocationReferenceConfigurationTemplate() { List source = BuildTemplateAggregates(); return SerializeLocationReferenceEntries(ownerNamesByPrefab: source.ToDictionary((TemplateAggregate aggregate) => aggregate.Prefab, (TemplateAggregate aggregate) => aggregate.OwnerName, StringComparer.OrdinalIgnoreCase), entries: (from aggregate in source.Where((TemplateAggregate aggregate) => aggregate.LocationPrefabs.Count > 0).OrderBy((TemplateAggregate aggregate) => aggregate.Prefab, StringComparer.OrdinalIgnoreCase) select new SpawnerLocationReferenceEntry { Prefab = aggregate.Prefab, Locations = aggregate.LocationPrefabs.OrderBy((string location) => location, StringComparer.OrdinalIgnoreCase).ToList() }).ToList()); } private static Dictionary> BuildLocationReferenceLookup() { Dictionary> dictionary = new Dictionary>(StringComparer.OrdinalIgnoreCase); foreach (var item3 in EnumerateLocationRootPrefabs()) { string item = item3.LocationPrefab; GameObject item2 = item3.RootPrefab; SpawnArea[] componentsInChildren = item2.GetComponentsInChildren(true); foreach (SpawnArea val in componentsInChildren) { if (!((Object)(object)val == (Object)null) && !((Object)(object)((Component)val).gameObject == (Object)null)) { AddLocationReferenceLocation(dictionary, GetLocationReferencePrefabName(((Component)val).gameObject), item); } } CreatureSpawner[] componentsInChildren2 = item2.GetComponentsInChildren(true); foreach (CreatureSpawner val2 in componentsInChildren2) { if (!((Object)(object)val2 == (Object)null) && !((Object)(object)((Component)val2).gameObject == (Object)null)) { AddLocationReferenceLocation(dictionary, GetLocationReferencePrefabName(((Component)val2).gameObject), item); } } } return dictionary; } private static string SerializeLocationReferenceEntries(IEnumerable entries, IReadOnlyDictionary ownerNamesByPrefab) { List> list = PrefabOutputSections.BuildSections(entries, (SpawnerLocationReferenceEntry entry) => entry.Prefab, (SpawnerLocationReferenceEntry entry) => ResolveSpawnerLocationReferenceOwnerName(entry, ownerNamesByPrefab)); foreach (PrefabOwnerSection item in list) { item.Entries.Sort((SpawnerLocationReferenceEntry left, SpawnerLocationReferenceEntry right) => StringComparer.OrdinalIgnoreCase.Compare(left.Prefab, right.Prefab)); } return PrefabOutputSections.SerializeReferenceSections(list, Serializer); } private static void WriteReferenceConfigurationFile(string? referenceContent, string? locationReferenceContent, string logMessage, bool writePrimaryReference, bool writeLocationReference) { if (writePrimaryReference && referenceContent != null) { GeneratedArtifactWriter.WriteText(ReferenceConfigurationPath, referenceContent); } if (writeLocationReference && locationReferenceContent != null) { GeneratedArtifactWriter.WriteText(LocationReferenceConfigurationPath, locationReferenceContent); } DropNSpawnPlugin.DropNSpawnLogger.LogInfo((object)logMessage); } private static void EnsureReferenceArtifactsUpToDate() { if (IsGameDataReady() && EnsureSpawnerReferenceConfigurationUpToDate()) { ResetReferenceSnapshots(); } } private static bool EnsureSpawnerReferenceConfigurationUpToDate() { string sourceSignature = ComputeReferenceSourceSignature(); ReferenceArtifactUpdateKind updateKind; bool flag = ReferenceArtifactLifecycle.TryPlanUpdate("spawner", ReferenceConfigurationPath, sourceSignature, out updateKind); ReferenceArtifactUpdateKind updateKind2; bool flag2 = ReferenceArtifactLifecycle.TryPlanUpdate("spawner.locations", LocationReferenceConfigurationPath, sourceSignature, out updateKind2); if (!flag && !flag2) { return false; } CaptureSnapshotsIfNeeded(); string referenceContent = (flag ? BuildReferenceConfigurationTemplate() : null); string locationReferenceContent = (flag2 ? BuildLocationReferenceConfigurationTemplate() : null); string text = ((updateKind == ReferenceArtifactUpdateKind.Updated || updateKind2 == ReferenceArtifactUpdateKind.Updated) ? "Updated" : "Created"); string text2 = ((flag && flag2) ? ("spawner reference configurations at " + ReferenceConfigurationPath + " and " + LocationReferenceConfigurationPath) : (flag ? ("spawner reference configuration at " + ReferenceConfigurationPath) : ("spawner location reference configuration at " + LocationReferenceConfigurationPath))); WriteReferenceConfigurationFile(referenceContent, locationReferenceContent, text + " " + text2 + ".", flag, flag2); if (flag) { ReferenceArtifactLifecycle.RecordUpdate("spawner", ReferenceConfigurationPath, sourceSignature); } if (flag2) { ReferenceArtifactLifecycle.RecordUpdate("spawner.locations", LocationReferenceConfigurationPath, sourceSignature); } return true; } private static IEnumerable BuildCurrentSpawnerReferencePrefabKeys() { HashSet seen = new HashSet(StringComparer.OrdinalIgnoreCase); foreach (GameObject rootPrefab in EnumerateRootPrefabs()) { SpawnArea[] componentsInChildren = rootPrefab.GetComponentsInChildren(true); foreach (SpawnArea obj in componentsInChildren) { object key; if (obj == null) { key = null; } else { GameObject gameObject = ((Component)obj).gameObject; key = ((gameObject != null) ? ((Object)gameObject).name : null); } string text = ReferenceRefreshSupport.NormalizeKey((string?)key); if (text.Length > 0 && seen.Add(text)) { yield return text; } } CreatureSpawner[] componentsInChildren2 = rootPrefab.GetComponentsInChildren(true); foreach (CreatureSpawner obj2 in componentsInChildren2) { object key2; if (obj2 == null) { key2 = null; } else { GameObject gameObject2 = ((Component)obj2).gameObject; key2 = ((gameObject2 != null) ? ((Object)gameObject2).name : null); } string text2 = ReferenceRefreshSupport.NormalizeKey((string?)key2); if (text2.Length > 0 && seen.Add(text2)) { yield return text2; } } } } private static string ComputeReferenceSourceSignature() { return ReferenceRefreshSupport.ComputeStableHashForKeys(BuildCurrentSpawnerReferencePrefabKeys()); } internal static bool TryWriteFullScaffoldConfigurationFile(out string path, out string error) { string content; string logMessage; lock (Sync) { path = FullScaffoldConfigurationPath; error = ""; if (!IsGameDataReady() && !_snapshotsCaptured) { error = "Spawner game data is not ready yet."; return false; } CaptureSnapshotsIfNeeded(); content = BuildFullScaffoldConfigurationTemplate(); logMessage = "Wrote spawner full scaffold configuration to " + path + "."; } GeneratedArtifactWriter.WriteTextAlways(path, content, logMessage); return true; } internal static void RefreshReferenceConfigurationFile() { string referenceContent; string locationReferenceContent; string sourceSignature; string logMessage; lock (Sync) { if (!IsGameDataReady()) { return; } CaptureSnapshotsIfNeeded(); referenceContent = BuildReferenceConfigurationTemplate(); locationReferenceContent = BuildLocationReferenceConfigurationTemplate(); sourceSignature = ComputeReferenceSourceSignature(); logMessage = "Updated spawner reference configurations at " + ReferenceConfigurationPath + " and " + LocationReferenceConfigurationPath + "."; } WriteReferenceConfigurationFile(referenceContent, locationReferenceContent, logMessage, writePrimaryReference: true, writeLocationReference: true); ReferenceArtifactLifecycle.RecordUpdate("spawner", ReferenceConfigurationPath, sourceSignature); ReferenceArtifactLifecycle.RecordUpdate("spawner.locations", LocationReferenceConfigurationPath, sourceSignature); lock (Sync) { ResetReferenceSnapshots(); } } private static IEnumerable<(string LocationPrefab, GameObject RootPrefab)> EnumerateLocationRootPrefabs() { if ((Object)(object)ZoneSystem.instance == (Object)null) { yield break; } HashSet seen = new HashSet(StringComparer.OrdinalIgnoreCase); foreach (ZoneLocation location in ZoneSystem.instance.m_locations) { if (!location.m_prefab.IsValid) { continue; } string text = (location.m_prefab.Name ?? "").Trim(); if (text.Length != 0 && seen.Add(text)) { location.m_prefab.Load(); GameObject asset = location.m_prefab.Asset; if (!((Object)(object)asset == (Object)null)) { yield return (LocationPrefab: text, RootPrefab: asset); } } } } private static void AddLocationReferenceLocation(Dictionary> groupedLocations, string configPrefabName, string locationPrefab) { string text = (configPrefabName ?? "").Trim(); string text2 = (locationPrefab ?? "").Trim(); if (text.Length != 0 && text2.Length != 0) { if (!groupedLocations.TryGetValue(text, out SortedSet value)) { value = (groupedLocations[text] = new SortedSet(StringComparer.OrdinalIgnoreCase)); } value.Add(text2); } } private static SpawnerConfigurationEntry BuildConfigurationEntry(TemplateAggregate aggregate) { return new SpawnerConfigurationEntry { Prefab = aggregate.Prefab, Enabled = true, SpawnArea = ((aggregate.SpawnArea != null) ? ConvertSpawnArea(aggregate.SpawnArea) : null), CreatureSpawner = ((aggregate.CreatureSpawner != null) ? ConvertCreatureSpawner(aggregate.CreatureSpawner) : null) }; } private static void TrackAggregateRootPrefab(TemplateAggregate aggregate, string rootPrefabName) { string text = (rootPrefabName ?? "").Trim(); if (text.Length > 0) { aggregate.RootPrefabs.Add(text); } } private static void TrackAggregateLocationPrefabs(TemplateAggregate aggregate, Dictionary> locationPrefabsBySpawnerPrefab, string configPrefabName) { string text = (configPrefabName ?? "").Trim(); if (text.Length == 0 || !locationPrefabsBySpawnerPrefab.TryGetValue(text, out SortedSet value)) { return; } foreach (string item in value) { if (!string.IsNullOrWhiteSpace(item)) { aggregate.LocationPrefabs.Add(item.Trim()); } } } private static string ResolveSpawnerOwnerName(string prefabName, IEnumerable locationPrefabs, IEnumerable rootPrefabs) { PrefabOwnerResolver.OwnerSnapshot snapshot = PrefabOwnerResolver.GetSnapshot(); List list = (from ownerName in locationPrefabs.Select(snapshot.GetOwnerName) where !string.Equals(ownerName, "Unknown / Untracked", StringComparison.OrdinalIgnoreCase) select ownerName).Distinct(StringComparer.OrdinalIgnoreCase).ToList(); if (list.Count == 1) { return list[0]; } if (list.Count > 1) { return "Unknown / Untracked"; } List list2 = (from ownerName in rootPrefabs.Select(snapshot.GetOwnerName) where !string.Equals(ownerName, "Unknown / Untracked", StringComparison.OrdinalIgnoreCase) select ownerName).Distinct(StringComparer.OrdinalIgnoreCase).ToList(); if (list2.Count == 1) { return list2[0]; } if (list2.Count > 1) { return "Unknown / Untracked"; } return snapshot.GetOwnerName(prefabName); } private static string ResolveSpawnerLocationReferenceOwnerName(SpawnerLocationReferenceEntry entry, IReadOnlyDictionary ownerNamesByPrefab) { if (ownerNamesByPrefab.TryGetValue(entry.Prefab, out string value) && !string.IsNullOrWhiteSpace(value)) { return value; } return ResolveSpawnerOwnerName(entry.Prefab, entry.Locations ?? new List(), Array.Empty()); } private static int CompareSpawnerAggregatesForOutput(TemplateAggregate? left, TemplateAggregate? right) { if (left == right) { return 0; } if (left == null) { return 1; } if (right == null) { return -1; } int num = GetSpawnerPrimaryComponentRank(left).CompareTo(GetSpawnerPrimaryComponentRank(right)); if (num != 0) { return num; } int num2 = GetSpawnerComponentSignatureMask(left).CompareTo(GetSpawnerComponentSignatureMask(right)); if (num2 != 0) { return num2; } return string.Compare(left.Prefab, right.Prefab, StringComparison.OrdinalIgnoreCase); } private static int GetSpawnerPrimaryComponentRank(TemplateAggregate aggregate) { if (aggregate.SpawnArea != null) { return 0; } if (aggregate.CreatureSpawner != null) { return 1; } return 2; } private static int GetSpawnerComponentSignatureMask(TemplateAggregate aggregate) { int num = 0; if (aggregate.SpawnArea != null) { num |= 1; } if (aggregate.CreatureSpawner != null) { num |= 2; } return num; } private static SpawnAreaDefinition ConvertSpawnArea(SpawnAreaComponentSnapshot snapshot) { List list = (from entry in snapshot.Prefabs.Select(delegate(SpawnAreaSpawnSnapshot prefab) { GameObject? prefab2 = prefab.Prefab; return new { Name = (((prefab2 != null) ? ((Object)prefab2).name : null) ?? ""), Prefab = prefab }; }) where !string.IsNullOrWhiteSpace(entry.Name) select new SpawnAreaSpawnDefinition { Creature = entry.Name, Weight = (IsReferenceDefault(entry.Prefab.Weight, 1f) ? ((float?)null) : new float?(entry.Prefab.Weight)), Level = RangeFormatting.FromReference(entry.Prefab.MinLevel, entry.Prefab.MaxLevel, 1, 1) }).ToList(); return new SpawnAreaDefinition { LevelUpChance = (IsReferenceDefault(snapshot.LevelUpChance, 15f) ? ((float?)null) : new float?(snapshot.LevelUpChance)), SpawnInterval = (IsReferenceDefault(snapshot.SpawnInterval, 30f) ? ((float?)null) : new float?(snapshot.SpawnInterval)), TriggerDistance = (IsReferenceDefault(snapshot.TriggerDistance, 256f) ? ((float?)null) : new float?(snapshot.TriggerDistance)), SetPatrolSpawnPoint = (snapshot.SetPatrolSpawnPoint ? ((bool?)null) : new bool?(false)), SpawnRadius = (IsReferenceDefault(snapshot.SpawnRadius, 2f) ? ((float?)null) : new float?(snapshot.SpawnRadius)), NearRadius = (IsReferenceDefault(snapshot.NearRadius, 10f) ? ((float?)null) : new float?(snapshot.NearRadius)), FarRadius = (IsReferenceDefault(snapshot.FarRadius, 1000f) ? ((float?)null) : new float?(snapshot.FarRadius)), MaxNear = ((snapshot.MaxNear == 3) ? ((int?)null) : new int?(snapshot.MaxNear)), MaxTotal = ((snapshot.MaxTotal == 20) ? ((int?)null) : new int?(snapshot.MaxTotal)), OnGroundOnly = (snapshot.OnGroundOnly ? new bool?(true) : ((bool?)null)), Creatures = ((list.Count > 0) ? list : null) }; } private static CreatureSpawnerDefinition ConvertCreatureSpawner(CreatureSpawnerComponentSnapshot snapshot) { return new CreatureSpawnerDefinition { Creature = (((Object)(object)snapshot.CreaturePrefab != (Object)null) ? ((Object)snapshot.CreaturePrefab).name : null), Level = RangeFormatting.FromReference(snapshot.MinLevel, snapshot.MaxLevel, 1, 1), LevelUpChance = (IsReferenceDefault(snapshot.LevelUpChance, 10f) ? ((float?)null) : new float?(snapshot.LevelUpChance)), RespawnTimeMinutes = (IsReferenceDefault(snapshot.RespawnTimeMinutes, 20f) ? ((float?)null) : new float?(snapshot.RespawnTimeMinutes)), TriggerDistance = (IsReferenceDefault(snapshot.TriggerDistance, 60f) ? ((float?)null) : new float?(snapshot.TriggerDistance)), TriggerNoise = (IsReferenceDefault(snapshot.TriggerNoise, 0f) ? ((float?)null) : new float?(snapshot.TriggerNoise)), TimeOfDay = ((snapshot.SpawnAtDay && snapshot.SpawnAtNight) ? null : TimeOfDayFormatting.FromSpawnFlags(snapshot.SpawnAtDay, snapshot.SpawnAtNight)), AllowInsidePlayerBase = (snapshot.SpawnInPlayerBase ? new bool?(true) : ((bool?)null)), WakeUpAnimation = (snapshot.WakeUpAnimation ? new bool?(true) : ((bool?)null)), SpawnCheckInterval = ((snapshot.SpawnCheckInterval == 5) ? ((int?)null) : new int?(snapshot.SpawnCheckInterval)), RequiredGlobalKey = ((snapshot.RequiredGlobalKey.Length > 0) ? snapshot.RequiredGlobalKey : null), BlockingGlobalKey = ((snapshot.BlockingGlobalKey.Length > 0) ? snapshot.BlockingGlobalKey : null), SetPatrolSpawnPoint = (snapshot.SetPatrolSpawnPoint ? new bool?(true) : ((bool?)null)), SpawnGroupId = ((snapshot.SpawnGroupId == 0) ? ((int?)null) : new int?(snapshot.SpawnGroupId)), MaxGroupSpawned = ((snapshot.MaxGroupSpawned == 1) ? ((int?)null) : new int?(snapshot.MaxGroupSpawned)), SpawnGroupRadius = (IsReferenceDefault(snapshot.SpawnGroupRadius, 0f) ? ((float?)null) : new float?(snapshot.SpawnGroupRadius)), SpawnerWeight = (IsReferenceDefault(snapshot.SpawnerWeight, 1f) ? ((float?)null) : new float?(snapshot.SpawnerWeight)) }; } internal static bool PrepareSpawnAreaForUpdate(SpawnArea spawnArea) { ReconcileSpawnAreaRuntimeCore(spawnArea); return PrepareSpawnAreaTotalSpawnLimit(spawnArea); } internal static bool PrepareCreatureSpawnerForUpdate(CreatureSpawner creatureSpawner) { return PrepareCreatureSpawnerForUpdateCore(creatureSpawner); } private static void ReconcileSpawnAreaRuntimeCore(SpawnArea? spawnArea) { if ((Object)(object)spawnArea == (Object)null) { return; } if (!ShouldApplyLocally()) { ClearSpawnAreaDynamicRuntimeState(spawnArea); return; } SpawnerRuntimeConfigurationSnapshot runtimeConfigurationSnapshot = GetRuntimeConfigurationSnapshot(); if (!TryGetTrackedOrCurrentSpawnAreaEligibility(spawnArea, runtimeConfigurationSnapshot, out string configPrefabName, out bool _, out bool runtimeEligible) || !runtimeEligible) { ClearSpawnAreaDynamicRuntimeState(spawnArea); return; } TrackSpawnAreaInstanceInternal(spawnArea); if (!IsGameDataReady()) { return; } if (!TryGetActiveSpawnAreaEntryCache(spawnArea, runtimeConfigurationSnapshot, out MatchingEntryCache entryCache, out string _)) { ClearSpawnAreaDynamicRuntimeState(spawnArea, clearMatchingCache: true); return; } if (entryCache.RuntimeEntries.Count == 0) { ClearSpawnAreaDynamicRuntimeState(spawnArea, clearMatchingCache: true); return; } LocalRuntimeState orCreateLocalRuntimeState = RuntimeStateStore.GetOrCreateLocalRuntimeState(spawnArea); RuntimeContextSnapshot runtimeContextSnapshot = GetRuntimeContextSnapshot(); if (!ShouldEvaluateRuntimeState(entryCache, orCreateLocalRuntimeState, runtimeContextSnapshot) && RuntimeStateStore.HasRuntimeSignature(spawnArea)) { return; } ArmNextRuntimeEvaluationWindow(entryCache, orCreateLocalRuntimeState, runtimeContextSnapshot); int num = ComputeRuntimeConditionSignature(((Component)spawnArea).gameObject, entryCache, orCreateLocalRuntimeState, runtimeContextSnapshot); if (!RuntimeStateStore.TryGetRuntimeSignature(spawnArea, out var signature) || signature != num) { TrySelectWinningSpawnerEntryForRuntime(((Component)spawnArea).gameObject, entryCache, forSpawnArea: true, runtimeContextSnapshot, orCreateLocalRuntimeState, num, out SpawnerRuntimeEntry winningEntry); if (HasMatchingAppliedWinningEntry(orCreateLocalRuntimeState, entryCache, configPrefabName, winningEntry)) { RuntimeStateStore.SetRuntimeSignature(spawnArea, num); return; } ReconcileSpawnAreaInstanceInternal(spawnArea, null, entryCache, usePreselectedWinner: true, winningEntry); RecordAppliedWinningEntry(orCreateLocalRuntimeState, entryCache, configPrefabName, winningEntry); } } private static void ReconcileCreatureSpawnerRuntimeCore(CreatureSpawner? creatureSpawner) { if ((Object)(object)creatureSpawner == (Object)null) { return; } if (!ShouldApplyLocally()) { ClearCreatureSpawnerDynamicRuntimeState(creatureSpawner); return; } SpawnerRuntimeConfigurationSnapshot runtimeConfigurationSnapshot = GetRuntimeConfigurationSnapshot(); if (!TryGetTrackedOrCurrentCreatureSpawnerEligibility(creatureSpawner, runtimeConfigurationSnapshot, out string configPrefabName, out bool _, out bool runtimeEligible) || !runtimeEligible) { ClearCreatureSpawnerDynamicRuntimeState(creatureSpawner); return; } TrackCreatureSpawnerInstanceInternal(creatureSpawner); if (!IsGameDataReady()) { return; } if (!TryGetActiveCreatureSpawnerEntryCache(creatureSpawner, runtimeConfigurationSnapshot, out MatchingEntryCache entryCache, out string _)) { ClearCreatureSpawnerDynamicRuntimeState(creatureSpawner, clearMatchingCache: true); return; } if (entryCache.RuntimeEntries.Count == 0) { ClearCreatureSpawnerDynamicRuntimeState(creatureSpawner, clearMatchingCache: true); return; } LocalRuntimeState orCreateLocalRuntimeState = RuntimeStateStore.GetOrCreateLocalRuntimeState(creatureSpawner); RuntimeContextSnapshot runtimeContextSnapshot = GetRuntimeContextSnapshot(); if (!ShouldEvaluateRuntimeState(entryCache, orCreateLocalRuntimeState, runtimeContextSnapshot) && RuntimeStateStore.HasRuntimeSignature(creatureSpawner)) { return; } ArmNextRuntimeEvaluationWindow(entryCache, orCreateLocalRuntimeState, runtimeContextSnapshot); int num = ComputeRuntimeConditionSignature(((Component)creatureSpawner).gameObject, entryCache, orCreateLocalRuntimeState, runtimeContextSnapshot); if (!RuntimeStateStore.TryGetRuntimeSignature(creatureSpawner, out var signature) || signature != num) { TrySelectWinningSpawnerEntryForRuntime(((Component)creatureSpawner).gameObject, entryCache, forSpawnArea: false, runtimeContextSnapshot, orCreateLocalRuntimeState, num, out SpawnerRuntimeEntry winningEntry); if (HasMatchingAppliedWinningEntry(orCreateLocalRuntimeState, entryCache, configPrefabName, winningEntry)) { RuntimeStateStore.SetRuntimeSignature(creatureSpawner, num); return; } ReconcileCreatureSpawnerInstanceInternal(creatureSpawner, null, entryCache, usePreselectedWinner: true, winningEntry); RecordAppliedWinningEntry(orCreateLocalRuntimeState, entryCache, configPrefabName, winningEntry); } } private static bool PrepareCreatureSpawnerForUpdateCore(CreatureSpawner? creatureSpawner) { ReconcileCreatureSpawnerRuntimeCore(creatureSpawner); if ((Object)(object)creatureSpawner == (Object)null) { return true; } if (LiveReconcilerState.TryGetAppliedCreatureSpawnerTimeOfDay(creatureSpawner, out TimeOfDayDefinition timeOfDay) && !TimeOfDayFormatting.MatchesCurrentTime(timeOfDay)) { return false; } return true; } private static void ClearRuntimeReconcileState() { RuntimeStateStore.ClearDynamicCaches(); SelectorCacheStore.ClearSharedMatchingEntryTemplates(); } private static void UpdateSpawnAreaRuntimeSignature(SpawnArea spawnArea, IEnumerable? entries, MatchingEntryCache? entryCache = null) { IReadOnlyList readOnlyList = entryCache?.RuntimeEntries ?? entries?.Where((SpawnerRuntimeEntry entry) => entry.RuntimeReconcile).ToList() ?? new List(); if (readOnlyList.Count == 0) { ClearSpawnAreaDynamicRuntimeState(spawnArea); } else { RuntimeStateStore.SetRuntimeSignature(spawnArea, (entryCache != null) ? ComputeSpawnAreaRuntimeSignature(spawnArea, entryCache) : ComputeSpawnAreaRuntimeSignature(spawnArea, readOnlyList)); } } private static void UpdateCreatureSpawnerRuntimeSignature(CreatureSpawner creatureSpawner, IEnumerable? entries, MatchingEntryCache? entryCache = null) { IReadOnlyList readOnlyList = entryCache?.RuntimeEntries ?? entries?.Where((SpawnerRuntimeEntry entry) => entry.RuntimeReconcile).ToList() ?? new List(); if (readOnlyList.Count == 0) { ClearCreatureSpawnerDynamicRuntimeState(creatureSpawner); } else { RuntimeStateStore.SetRuntimeSignature(creatureSpawner, (entryCache != null) ? ComputeCreatureSpawnerRuntimeSignature(creatureSpawner, entryCache) : ComputeCreatureSpawnerRuntimeSignature(creatureSpawner, readOnlyList)); } } private static int ComputeSpawnAreaRuntimeSignature(SpawnArea spawnArea, MatchingEntryCache entryCache) { return ComputeRuntimeConditionSignature(((Component)spawnArea).gameObject, entryCache, RuntimeStateStore.GetOrCreateLocalRuntimeState(spawnArea)); } private static int ComputeCreatureSpawnerRuntimeSignature(CreatureSpawner creatureSpawner, MatchingEntryCache entryCache) { return ComputeRuntimeConditionSignature(((Component)creatureSpawner).gameObject, entryCache, RuntimeStateStore.GetOrCreateLocalRuntimeState(creatureSpawner)); } private static int ComputeSpawnAreaRuntimeSignature(SpawnArea spawnArea, IEnumerable entries) { int num = 17; foreach (SpawnerRuntimeEntry item in entries ?? Enumerable.Empty()) { if (item.RuntimeReconcile) { num = CombineRuntimeSignature(num, item.RuleId); bool value = !HasRuntimeEntryConditions(item) || DropConditionEvaluator.AreSatisfied(((Component)spawnArea).gameObject, item.Conditions); num = CombineRuntimeSignature(num, value); } } return num; } private static int ComputeRuntimeConditionSignature(GameObject gameObject, MatchingEntryCache entryCache, LocalRuntimeState localRuntimeState) { return ComputeRuntimeConditionSignature(gameObject, entryCache, localRuntimeState, GetRuntimeContextSnapshot()); } private static int ComputeRuntimeConditionSignature(GameObject gameObject, MatchingEntryCache entryCache, LocalRuntimeState localRuntimeState, RuntimeContextSnapshot runtimeContext) { int num = 17; foreach (SpawnerRuntimeEntry runtimeEntry in entryCache.RuntimeEntries) { num = CombineRuntimeSignature(num, runtimeEntry.RuleId); } if (entryCache.UsesTimeOfDay) { num = CombineRuntimeSignature(num, runtimeContext.TimeOfDayPhaseMarker); } if (entryCache.UsesRequiredEnvironments) { num = CombineRuntimeSignature(num, runtimeContext.EnvironmentName); } if (entryCache.UsesInsidePlayerBase) { num = CombineRuntimeSignature(num, GetInsidePlayerBaseState(gameObject, localRuntimeState)); } foreach (string runtimeRequiredGlobalKey in entryCache.RuntimeRequiredGlobalKeys) { num = CombineRuntimeSignature(num, runtimeRequiredGlobalKey); num = CombineRuntimeSignature(num, GetGlobalKeyState(runtimeContext, runtimeRequiredGlobalKey)); } foreach (string runtimeForbiddenGlobalKey in entryCache.RuntimeForbiddenGlobalKeys) { num = CombineRuntimeSignature(num, runtimeForbiddenGlobalKey); num = CombineRuntimeSignature(num, GetGlobalKeyState(runtimeContext, runtimeForbiddenGlobalKey)); } return num; } private static int ComputeCreatureSpawnerRuntimeSignature(CreatureSpawner creatureSpawner, IEnumerable entries) { int num = 17; foreach (SpawnerRuntimeEntry item in entries ?? Enumerable.Empty()) { if (item.RuntimeReconcile) { num = CombineRuntimeSignature(num, item.RuleId); bool value = !HasRuntimeEntryConditions(item) || DropConditionEvaluator.AreSatisfied(((Component)creatureSpawner).gameObject, item.Conditions); num = CombineRuntimeSignature(num, value); } } return num; } private static void ClearSpawnAreaDynamicRuntimeState(SpawnArea? spawnArea, bool clearMatchingCache = false) { if (!((Object)(object)spawnArea == (Object)null)) { RuntimeStateStore.RemoveRuntimeSignature(spawnArea); RuntimeStateStore.RemoveLocalRuntimeState(spawnArea); if (clearMatchingCache) { SelectorCacheStore.RemoveSpawnAreaEntryCache(spawnArea); } } } private static void ClearCreatureSpawnerDynamicRuntimeState(CreatureSpawner? creatureSpawner, bool clearMatchingCache = false) { if (!((Object)(object)creatureSpawner == (Object)null)) { RuntimeStateStore.RemoveRuntimeSignature(creatureSpawner); RuntimeStateStore.RemoveLocalRuntimeState(creatureSpawner); if (clearMatchingCache) { SelectorCacheStore.RemoveCreatureSpawnerEntryCache(creatureSpawner); } } } private static bool HasMatchingAppliedWinningEntry(LocalRuntimeState localRuntimeState, MatchingEntryCache entryCache, string configPrefabName, SpawnerRuntimeEntry? winningEntry) { if (!localRuntimeState.HasAppliedWinningEntrySelection) { return false; } if (!string.Equals(localRuntimeState.LastAppliedConfigPrefabName, configPrefabName, StringComparison.OrdinalIgnoreCase)) { return false; } if (!string.Equals(localRuntimeState.LastAppliedResolvedLocationKey, entryCache.ResolvedLocationKey, StringComparison.OrdinalIgnoreCase)) { return false; } string b = winningEntry?.RuleId ?? ""; return string.Equals(localRuntimeState.LastAppliedWinningEntryRuleId, b, StringComparison.Ordinal); } private static void RecordAppliedWinningEntry(LocalRuntimeState localRuntimeState, MatchingEntryCache entryCache, string configPrefabName, SpawnerRuntimeEntry? winningEntry) { localRuntimeState.HasAppliedWinningEntrySelection = true; localRuntimeState.LastAppliedConfigPrefabName = configPrefabName ?? ""; localRuntimeState.LastAppliedResolvedLocationKey = entryCache.ResolvedLocationKey ?? ""; localRuntimeState.LastAppliedWinningEntryRuleId = winningEntry?.RuleId ?? ""; } private static int CombineRuntimeSignature(int current, bool value) { return current * 31 + (value ? 1 : 0); } private static int CombineRuntimeSignature(int current, int value) { return current * 31 + value; } private static int CombineRuntimeSignature(int current, string value) { return current * 31 + (value?.GetHashCode() ?? 0); } private static RuntimeContextSnapshot GetRuntimeContextSnapshot() { int frameCount = Time.frameCount; if (RuntimeStateStore.TryGetRuntimeContextSnapshot(frameCount, out RuntimeContextSnapshot snapshot)) { return snapshot; } RuntimeContextSnapshot obj = new RuntimeContextSnapshot { Frame = frameCount, TimeOfDayPhaseMarker = TimeOfDayFormatting.GetCurrentRuntimePhaseMarker() }; EnvMan instance = EnvMan.instance; obj.EnvironmentName = ((instance == null) ? null : instance.GetCurrentEnvironment()?.m_name) ?? ""; RuntimeContextSnapshot runtimeContextSnapshot = obj; return RuntimeStateStore.SetRuntimeContextSnapshot(runtimeContextSnapshot); } private static bool GetInsidePlayerBaseState(GameObject gameObject, LocalRuntimeState localRuntimeState) { //IL_001c: Unknown result type (might be due to invalid IL or missing references) float realtimeSinceStartup = Time.realtimeSinceStartup; if (realtimeSinceStartup - localRuntimeState.LastInsidePlayerBaseSampleTime >= 0.5f) { localRuntimeState.IsInsidePlayerBase = (Object)(object)EffectArea.IsPointInsideArea(gameObject.transform.position, (Type)4, 0f) != (Object)null; localRuntimeState.LastInsidePlayerBaseSampleTime = realtimeSinceStartup; } return localRuntimeState.IsInsidePlayerBase; } private static bool GetGlobalKeyState(RuntimeContextSnapshot runtimeContext, string key) { if (runtimeContext.GlobalKeyStates.TryGetValue(key, out var value)) { return value; } value = (Object)(object)ZoneSystem.instance != (Object)null && ZoneSystem.instance.GetGlobalKey(key); runtimeContext.GlobalKeyStates[key] = value; return value; } private static bool ShouldEvaluateRuntimeState(MatchingEntryCache entryCache, LocalRuntimeState localRuntimeState, RuntimeContextSnapshot runtimeContext) { if (localRuntimeState.NextRuntimeEvaluationTime == float.NegativeInfinity) { return true; } if (entryCache.UsesTimeOfDay && localRuntimeState.LastObservedTimeOfDayPhaseMarker != runtimeContext.TimeOfDayPhaseMarker) { return true; } if (entryCache.UsesRequiredEnvironments && !string.Equals(localRuntimeState.LastObservedEnvironmentName, runtimeContext.EnvironmentName, StringComparison.Ordinal)) { return true; } return Time.realtimeSinceStartup >= localRuntimeState.NextRuntimeEvaluationTime; } private static void ArmNextRuntimeEvaluationWindow(MatchingEntryCache entryCache, LocalRuntimeState localRuntimeState, RuntimeContextSnapshot runtimeContext) { localRuntimeState.LastObservedTimeOfDayPhaseMarker = runtimeContext.TimeOfDayPhaseMarker; localRuntimeState.LastObservedEnvironmentName = runtimeContext.EnvironmentName; float num = ((entryCache.UsesInsidePlayerBase && !entryCache.UsesTimeOfDay && !entryCache.UsesRequiredEnvironments && entryCache.RuntimeRequiredGlobalKeys.Count == 0 && entryCache.RuntimeForbiddenGlobalKeys.Count == 0) ? 0.5f : 0.25f); localRuntimeState.NextRuntimeEvaluationTime = Time.realtimeSinceStartup + num; } private static bool AreRuntimeSpawnerConditionsSatisfied(GameObject gameObject, ConditionsDefinition? conditions, RuntimeContextSnapshot runtimeContext, LocalRuntimeState localRuntimeState) { return DropConditionEvaluator.AreDynamicConditionsSatisfied(conditions, runtimeContext.TimeOfDayPhaseMarker, runtimeContext.EnvironmentName, GetInsidePlayerBaseState(gameObject, localRuntimeState), (string key) => GetGlobalKeyState(runtimeContext, key)); } private static string BuildFullScaffoldConfigurationTemplate() { StringBuilder stringBuilder = new StringBuilder(); bool flag = false; foreach (PrefabOwnerSection item in BuildConfigurationTemplate()) { foreach (SpawnerConfigurationEntry entry in item.Entries) { if (flag) { AppendScaffoldBlankLine(stringBuilder); } AppendScaffoldListEntryLine(stringBuilder, 0, "prefab", entry.Prefab); AppendScaffoldLine(stringBuilder, 1, "enabled: " + FormatYamlBool(entry.Enabled)); AppendScaffoldLine(stringBuilder, 1, "locations: []"); AppendScaffoldConditionsBlock(stringBuilder, 1, entry.SpawnArea != null); if (entry.SpawnArea != null) { AppendScaffoldSpawnAreaBlock(stringBuilder, entry.SpawnArea); } if (entry.CreatureSpawner != null) { AppendScaffoldCreatureSpawnerBlock(stringBuilder, entry.CreatureSpawner); } flag = true; } } if (!flag) { return "[]" + Environment.NewLine; } return stringBuilder.ToString(); } private static void AppendScaffoldSpawnAreaBlock(StringBuilder builder, SpawnAreaDefinition definition) { AppendScaffoldLine(builder, 1, "spawnArea:"); AppendScaffoldLine(builder, 2, "levelUpChance: " + FormatYamlFloat(definition.LevelUpChance ?? 15f)); AppendScaffoldLine(builder, 2, "spawnInterval: " + FormatYamlFloat(definition.SpawnInterval ?? 30f)); AppendScaffoldLine(builder, 2, "triggerDistance: " + FormatYamlFloat(definition.TriggerDistance ?? 256f)); AppendScaffoldLine(builder, 2, "setPatrolSpawnPoint: " + FormatYamlBool(definition.SetPatrolSpawnPoint ?? true)); AppendScaffoldLine(builder, 2, "spawnRadius: " + FormatYamlFloat(definition.SpawnRadius ?? 2f)); AppendScaffoldLine(builder, 2, "nearRadius: " + FormatYamlFloat(definition.NearRadius ?? 10f)); AppendScaffoldLine(builder, 2, "farRadius: " + FormatYamlFloat(definition.FarRadius ?? 1000f)); AppendScaffoldLine(builder, 2, $"maxNear: {definition.MaxNear ?? 3}"); AppendScaffoldLine(builder, 2, $"maxTotal: {definition.MaxTotal ?? 20}"); AppendScaffoldLine(builder, 2, $"maxTotalSpawns: {definition.MaxTotalSpawns.GetValueOrDefault()}"); AppendScaffoldLine(builder, 2, "onGroundOnly: " + FormatYamlBool(definition.OnGroundOnly == true)); List list = definition.Creatures ?? new List(); if (list.Count > 0) { AppendScaffoldLine(builder, 2, "creatures:"); { foreach (SpawnAreaSpawnDefinition item in list) { AppendScaffoldListEntryLine(builder, 3, "creature", item.Creature); AppendScaffoldLine(builder, 4, "weight: " + FormatYamlFloat(item.Weight ?? 1f)); AppendScaffoldLine(builder, 4, "level: " + RangeFormatting.FormatInlineObject(GetLevelRange(item) ?? RangeFormatting.From(1, 1))); AppendScaffoldStringLine(builder, 4, "faction", item.Faction); AppendScaffoldStringLine(builder, 4, "data", item.Data); AppendScaffoldLine(builder, 4, "fields: {}"); AppendScaffoldLine(builder, 4, "objects: []"); } return; } } AppendScaffoldLine(builder, 2, "creatures: []"); } private static void AppendScaffoldCreatureSpawnerBlock(StringBuilder builder, CreatureSpawnerDefinition definition) { AppendScaffoldLine(builder, 1, "creatureSpawner:"); AppendScaffoldStringLine(builder, 2, "creature", definition.Creature); AppendScaffoldLine(builder, 2, "timeOfDay: " + TimeOfDayFormatting.FormatInlineList(definition.TimeOfDay, TimeOfDayFormatting.FromSpawnFlags(allowDay: true, allowNight: true))); AppendScaffoldStringLine(builder, 2, "requiredGlobalKey", definition.RequiredGlobalKey ?? ""); AppendScaffoldStringLine(builder, 2, "blockingGlobalKey", definition.BlockingGlobalKey ?? ""); AppendScaffoldLine(builder, 2, "level: " + RangeFormatting.FormatInlineObject(GetLevelRange(definition) ?? RangeFormatting.From(1, 1))); AppendScaffoldLine(builder, 2, "levelUpChance: " + FormatYamlFloat(definition.LevelUpChance ?? 10f)); AppendScaffoldLine(builder, 2, "respawnTimeMinutes: " + FormatYamlFloat(definition.RespawnTimeMinutes ?? 20f)); AppendScaffoldLine(builder, 2, $"spawnCheckInterval: {definition.SpawnCheckInterval ?? 5}"); AppendScaffoldLine(builder, 2, $"spawnGroupId: {definition.SpawnGroupId.GetValueOrDefault()}"); AppendScaffoldLine(builder, 2, "spawnGroupRadius: " + FormatYamlFloat(definition.SpawnGroupRadius.GetValueOrDefault())); AppendScaffoldLine(builder, 2, "spawnerWeight: " + FormatYamlFloat(definition.SpawnerWeight ?? 1f)); AppendScaffoldLine(builder, 2, $"maxGroupSpawned: {definition.MaxGroupSpawned ?? 1}"); AppendScaffoldLine(builder, 2, "triggerDistance: " + FormatYamlFloat(definition.TriggerDistance ?? 60f)); AppendScaffoldLine(builder, 2, "triggerNoise: " + FormatYamlFloat(definition.TriggerNoise.GetValueOrDefault())); AppendScaffoldLine(builder, 2, "allowInsidePlayerBase: " + FormatYamlBool(definition.AllowInsidePlayerBase == true)); AppendScaffoldLine(builder, 2, "wakeUpAnimation: " + FormatYamlBool(definition.WakeUpAnimation == true)); AppendScaffoldLine(builder, 2, "setPatrolSpawnPoint: " + FormatYamlBool(definition.SetPatrolSpawnPoint == true)); AppendScaffoldStringLine(builder, 2, "faction", definition.Faction); AppendScaffoldStringLine(builder, 2, "data", definition.Data); AppendScaffoldLine(builder, 2, "fields: {}"); AppendScaffoldLine(builder, 2, "objects: []"); } private static IntRangeDefinition? GetLevelRange(SpawnAreaSpawnDefinition definition) { return definition.Level ?? RangeFormatting.From(definition.MinLevel, definition.MaxLevel ?? definition.MinLevel); } private static IntRangeDefinition? GetLevelRange(CreatureSpawnerDefinition definition) { return definition.Level ?? RangeFormatting.From(definition.MinLevel, definition.MaxLevel ?? definition.MinLevel); } private static void AppendScaffoldLine(StringBuilder builder, int indent, string text) { builder.Append(' ', indent * 2); builder.AppendLine(text); } private static void AppendScaffoldBlankLine(StringBuilder builder) { builder.AppendLine(); } private static void AppendScaffoldConditionsBlock(StringBuilder builder, int indent, bool includeSpawnAreaOnlyFields) { AppendScaffoldLine(builder, indent, "conditions:"); AppendScaffoldLine(builder, indent + 1, "altitude: null"); AppendScaffoldLine(builder, indent + 1, "distanceFromCenter: null"); AppendScaffoldLine(builder, indent + 1, "biomes: []"); if (includeSpawnAreaOnlyFields) { AppendScaffoldLine(builder, indent + 1, "timeOfDay: null"); } AppendScaffoldLine(builder, indent + 1, "requiredEnvironments: []"); if (includeSpawnAreaOnlyFields) { AppendScaffoldLine(builder, indent + 1, "requiredGlobalKeys: []"); AppendScaffoldLine(builder, indent + 1, "forbiddenGlobalKeys: []"); } AppendScaffoldLine(builder, indent + 1, "inForest: null"); AppendScaffoldLine(builder, indent + 1, "inDungeon: null"); if (includeSpawnAreaOnlyFields) { AppendScaffoldLine(builder, indent + 1, "insidePlayerBase: null"); } } private static void AppendScaffoldStringLine(StringBuilder builder, int indent, string key, string? value) { if (value == null) { AppendScaffoldLine(builder, indent, key + ": null"); } else { AppendScaffoldLine(builder, indent, key + ": " + FormatYamlString(value)); } } private static void AppendScaffoldListEntryLine(StringBuilder builder, int indent, string key, string? value) { if (value == null) { AppendScaffoldLine(builder, indent, "- " + key + ": null"); } else { AppendScaffoldLine(builder, indent, "- " + key + ": " + FormatYamlString(value)); } } private static bool TryGetActiveSpawnAreaEntryCache(SpawnArea? spawnArea, out MatchingEntryCache? entryCache, out string configPrefabName) { return TryGetActiveSpawnAreaEntryCache(spawnArea, GetRuntimeConfigurationSnapshot(), out entryCache, out configPrefabName); } private static bool TryGetActiveSpawnAreaEntryCache(SpawnArea? spawnArea, SpawnerRuntimeConfigurationSnapshot runtimeConfigurationSnapshot, out MatchingEntryCache? entryCache, out string configPrefabName) { entryCache = null; configPrefabName = ""; if ((Object)(object)spawnArea == (Object)null || (Object)(object)((Component)spawnArea).gameObject == (Object)null) { return false; } configPrefabName = (LiveRegistryStore.TryGetTrackedPrefabName(spawnArea, out string prefabName) ? prefabName : GetConfigPrefabName(((Component)spawnArea).gameObject, "SpawnArea")); if (configPrefabName.Length == 0) { return false; } if (SelectorCacheStore.TryGetSpawnAreaEntryCache(spawnArea, out entryCache) && IsMatchingEntryCacheValid(((Component)spawnArea).gameObject, configPrefabName, entryCache)) { if (!ShouldPersistMatchingEntryCache(entryCache)) { SelectorCacheStore.RemoveSpawnAreaEntryCache(spawnArea); ClearSpawnAreaDynamicRuntimeState(spawnArea); } return entryCache.Entries.Count > 0; } entryCache = BuildMatchingEntryCache(((Component)spawnArea).gameObject, configPrefabName, runtimeConfigurationSnapshot, forSpawnArea: true); if (ShouldPersistMatchingEntryCache(entryCache)) { SelectorCacheStore.SetSpawnAreaEntryCache(spawnArea, entryCache); } else { SelectorCacheStore.RemoveSpawnAreaEntryCache(spawnArea); ClearSpawnAreaDynamicRuntimeState(spawnArea); } return entryCache.Entries.Count > 0; } private static bool TryGetActiveCreatureSpawnerEntryCache(CreatureSpawner? creatureSpawner, out MatchingEntryCache? entryCache, out string configPrefabName) { return TryGetActiveCreatureSpawnerEntryCache(creatureSpawner, GetRuntimeConfigurationSnapshot(), out entryCache, out configPrefabName); } private static bool TryGetActiveCreatureSpawnerEntryCache(CreatureSpawner? creatureSpawner, SpawnerRuntimeConfigurationSnapshot runtimeConfigurationSnapshot, out MatchingEntryCache? entryCache, out string configPrefabName) { entryCache = null; configPrefabName = ""; if ((Object)(object)creatureSpawner == (Object)null || (Object)(object)((Component)creatureSpawner).gameObject == (Object)null) { return false; } configPrefabName = (LiveRegistryStore.TryGetTrackedPrefabName(creatureSpawner, out string prefabName) ? prefabName : GetConfigPrefabName(((Component)creatureSpawner).gameObject, "CreatureSpawner")); if (configPrefabName.Length == 0) { return false; } if (SelectorCacheStore.TryGetCreatureSpawnerEntryCache(creatureSpawner, out entryCache) && IsMatchingEntryCacheValid(((Component)creatureSpawner).gameObject, configPrefabName, entryCache)) { if (!ShouldPersistMatchingEntryCache(entryCache)) { SelectorCacheStore.RemoveCreatureSpawnerEntryCache(creatureSpawner); ClearCreatureSpawnerDynamicRuntimeState(creatureSpawner); } return entryCache.Entries.Count > 0; } entryCache = BuildMatchingEntryCache(((Component)creatureSpawner).gameObject, configPrefabName, runtimeConfigurationSnapshot, forSpawnArea: false); if (ShouldPersistMatchingEntryCache(entryCache)) { SelectorCacheStore.SetCreatureSpawnerEntryCache(creatureSpawner, entryCache); } else { SelectorCacheStore.RemoveCreatureSpawnerEntryCache(creatureSpawner); ClearCreatureSpawnerDynamicRuntimeState(creatureSpawner); } return entryCache.Entries.Count > 0; } private static MatchingEntryCache BuildMatchingEntryCache(GameObject gameObject, string configPrefabName, SpawnerRuntimeConfigurationSnapshot runtimeConfigurationSnapshot, bool forSpawnArea) { MatchingEntryCache matchingEntryCache = new MatchingEntryCache { ConfigPrefabName = configPrefabName }; if ((Object)(object)gameObject == (Object)null || string.IsNullOrWhiteSpace(configPrefabName)) { return matchingEntryCache; } if (forSpawnArea) { if (!runtimeConfigurationSnapshot.ConfiguredSpawnAreaPrefabs.Contains(configPrefabName)) { return matchingEntryCache; } } else if (!runtimeConfigurationSnapshot.ConfiguredCreatureSpawnerPrefabs.Contains(configPrefabName)) { return matchingEntryCache; } if (!runtimeConfigurationSnapshot.PlansByPrefab.TryGetValue(configPrefabName, out CompiledSpawnerPrefabPlan value)) { return matchingEntryCache; } List list = (forSpawnArea ? value.SpawnAreaEntries : value.CreatureSpawnerEntries); if (list.Count == 0) { return matchingEntryCache; } bool flag = list.Any(HasLocationSelector); string locationPrefab = ""; bool flag2 = !flag || TryGetLiveLocationContextForSelector(gameObject, out locationPrefab); matchingEntryCache.UsesLocationSelector = flag; matchingEntryCache.ResolvedLocationKey = (flag ? NormalizeSelectorLocationCacheKey(flag2 ? locationPrefab : null) : ""); CaptureRecordedLocationProvenanceEpoch(gameObject, matchingEntryCache); if (TryGetSharedMatchingEntryTemplate(gameObject, configPrefabName, list, forSpawnArea, flag2, flag2 ? locationPrefab : null, out SharedMatchingEntryTemplate sharedTemplate)) { MatchingEntryCache matchingEntryCache2 = new MatchingEntryCache(); matchingEntryCache2.UseSharedTemplate(sharedTemplate); CaptureRecordedLocationProvenanceEpoch(gameObject, matchingEntryCache2); return matchingEntryCache2; } foreach (SpawnerRuntimeEntry item in list) { if (TryGetLocationMatchSpecificity(gameObject, item.Locations, flag2, flag2 ? locationPrefab : null, out var _) && (!HasRuntimeEntryConditions(item) || DropConditionEvaluator.AreStaticConditionsSatisfied(gameObject, item.Conditions, flag2 ? locationPrefab : null))) { matchingEntryCache.MutableEntries.Add(item); if (item.RuntimeReconcile) { matchingEntryCache.MutableRuntimeEntries.Add(item); AccumulateRuntimeConditionUsage(matchingEntryCache, item.Conditions); } } } return matchingEntryCache; } private static bool TryGetSharedMatchingEntryTemplate(GameObject gameObject, string configPrefabName, List entries, bool forSpawnArea, bool hasResolvedLocation, string? resolvedLocationPrefab, out SharedMatchingEntryTemplate? sharedTemplate) { sharedTemplate = null; if (!TryBuildSharedMatchingEntryTemplateKey(gameObject, configPrefabName, entries, forSpawnArea, hasResolvedLocation, resolvedLocationPrefab, out StaticSelectorContextSnapshot contextSnapshot, out string cacheKey, out string resolvedLocationKey)) { return false; } if (SelectorCacheStore.TryGetSharedMatchingEntryTemplate(cacheKey, out sharedTemplate)) { return true; } sharedTemplate = BuildSharedMatchingEntryTemplate(gameObject, configPrefabName, entries, hasResolvedLocation, resolvedLocationPrefab, resolvedLocationKey, contextSnapshot); SelectorCacheStore.SetSharedMatchingEntryTemplate(cacheKey, sharedTemplate); return true; } private static bool TryBuildSharedMatchingEntryTemplateKey(GameObject gameObject, string configPrefabName, List entries, bool forSpawnArea, bool hasResolvedLocation, string? resolvedLocationPrefab, out StaticSelectorContextSnapshot contextSnapshot, out string cacheKey, out string resolvedLocationKey) { //IL_01ee: Unknown result type (might be due to invalid IL or missing references) //IL_01f8: Expected I4, but got Unknown contextSnapshot = null; cacheKey = ""; resolvedLocationKey = (hasResolvedLocation ? NormalizeSelectorLocationCacheKey(resolvedLocationPrefab) : ""); bool flag = false; bool flag2 = false; bool flag3 = false; bool flag4 = false; foreach (SpawnerRuntimeEntry entry in entries) { ConditionsDefinition conditions = entry.Conditions; if (!CanUseSharedMatchingEntryTemplate(conditions)) { return false; } flag |= HasLocationSelector(entry); flag2 |= conditions != null && conditions.Locations?.Any((string name) => !string.IsNullOrWhiteSpace(name)) == true; flag3 |= (conditions != null && conditions.ResolvedBiomeMask.HasValue) || (conditions != null && conditions.Biomes?.Any((string name) => !string.IsNullOrWhiteSpace(name)) == true); flag4 |= conditions?.InDungeon.HasValue ?? false; } contextSnapshot = GetOrBuildStaticSelectorContextSnapshot(gameObject, flag, flag2, flag3, flag4, hasResolvedLocation); StringBuilder stringBuilder = new StringBuilder(configPrefabName.Length + 64); stringBuilder.Append(forSpawnArea ? "spawnarea|" : "creaturespawner|"); stringBuilder.Append(configPrefabName); stringBuilder.Append("|selector:"); stringBuilder.Append(flag ? resolvedLocationKey : ""); if (flag2) { stringBuilder.Append("|condloc:"); stringBuilder.Append(contextSnapshot.ConditionLocationName); } if (flag3) { stringBuilder.Append("|biome:"); stringBuilder.Append((int)contextSnapshot.Biome); } if (flag4) { stringBuilder.Append("|dungeon:"); stringBuilder.Append(contextSnapshot.InDungeon ? '1' : '0'); } cacheKey = stringBuilder.ToString(); return true; } private static bool CanUseSharedMatchingEntryTemplate(ConditionsDefinition? conditions) { if (conditions != null) { if (!conditions.InForest.HasValue) { FloatRangeDefinition? distanceFromCenter = conditions.DistanceFromCenter; if ((distanceFromCenter == null || !distanceFromCenter.HasValues()) && !conditions.MinDistanceFromCenter.HasValue && !conditions.MaxDistanceFromCenter.HasValue) { FloatRangeDefinition? altitude = conditions.Altitude; if ((altitude == null || !altitude.HasValues()) && !conditions.MinAltitude.HasValue) { return !conditions.MaxAltitude.HasValue; } } } return false; } return true; } private static SharedMatchingEntryTemplate BuildSharedMatchingEntryTemplate(GameObject gameObject, string configPrefabName, List entries, bool hasResolvedLocation, string? resolvedLocationPrefab, string resolvedLocationKey, StaticSelectorContextSnapshot contextSnapshot) { SharedMatchingEntryTemplate sharedMatchingEntryTemplate = new SharedMatchingEntryTemplate { ConfigPrefabName = configPrefabName, UsesLocationSelector = entries.Any(HasLocationSelector), ResolvedLocationKey = resolvedLocationKey }; foreach (SpawnerRuntimeEntry entry in entries) { if (TryGetLocationMatchSpecificity(gameObject, entry.Locations, hasResolvedLocation, hasResolvedLocation ? resolvedLocationPrefab : null, out var _) && (!HasRuntimeEntryConditions(entry) || DropConditionEvaluator.AreStaticConditionsSatisfied(gameObject, entry.Conditions, contextSnapshot.ConditionLocationName))) { sharedMatchingEntryTemplate.Entries.Add(entry); if (entry.RuntimeReconcile) { sharedMatchingEntryTemplate.RuntimeEntries.Add(entry); AccumulateRuntimeConditionUsage(sharedMatchingEntryTemplate.RuntimeRequiredGlobalKeys, sharedMatchingEntryTemplate.RuntimeForbiddenGlobalKeys, entry.Conditions, out var usesTimeOfDay, out var usesRequiredEnvironments, out var usesInsidePlayerBase); sharedMatchingEntryTemplate.UsesTimeOfDay |= usesTimeOfDay; sharedMatchingEntryTemplate.UsesRequiredEnvironments |= usesRequiredEnvironments; sharedMatchingEntryTemplate.UsesInsidePlayerBase |= usesInsidePlayerBase; } } } return sharedMatchingEntryTemplate; } private static StaticSelectorContextSnapshot GetOrBuildStaticSelectorContextSnapshot(GameObject gameObject, bool usesSelector, bool usesConditionLocation, bool usesBiome, bool usesInDungeon, bool hasResolvedLocation) { //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_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_0070: 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_0068: 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) bool flag = usesSelector || usesConditionLocation; if (SelectorCacheStore.TryGetReusableStaticSelectorContext(gameObject, flag, out StaticSelectorContextSnapshot snapshot)) { return snapshot; } Vector3 position = gameObject.transform.position; StaticSelectorContextSnapshot staticSelectorContextSnapshot = new StaticSelectorContextSnapshot { Position = position }; if (usesSelector && !hasResolvedLocation) { TryGetLiveLocationContextForSelector(gameObject, out string _); } if (usesConditionLocation) { staticSelectorContextSnapshot.ConditionLocationName = (TryGetResolvedLocationNameForConditions(gameObject, out string locationPrefab2) ? locationPrefab2 : ""); } if (usesBiome) { WorldGenerator instance = WorldGenerator.instance; staticSelectorContextSnapshot.Biome = ((instance != null) ? instance.GetBiome(position) : Heightmap.FindBiome(position)); } if (usesInDungeon) { staticSelectorContextSnapshot.InDungeon = Character.InInterior(position); } if (flag && TryGetRecordedLocationProvenanceEpoch(gameObject, out var provenanceEpoch)) { staticSelectorContextSnapshot.HasRecordedLocationProvenanceEpoch = true; staticSelectorContextSnapshot.RecordedLocationProvenanceEpoch = provenanceEpoch; } SelectorCacheStore.StoreReusableStaticSelectorContext(gameObject, staticSelectorContextSnapshot, flag); return staticSelectorContextSnapshot; } private static bool ShouldPersistMatchingEntryCache(MatchingEntryCache? entryCache) { if (entryCache != null) { return entryCache.Entries.Count > 0; } return false; } private static void CaptureRecordedLocationProvenanceEpoch(GameObject gameObject, MatchingEntryCache cache) { cache.HasRecordedLocationProvenanceEpoch = false; cache.RecordedLocationProvenanceEpoch = 0; if (!((Object)(object)gameObject == (Object)null) && cache != null && cache.UsesLocationSelector && TryGetRecordedLocationProvenanceEpoch(gameObject, out var provenanceEpoch)) { cache.HasRecordedLocationProvenanceEpoch = true; cache.RecordedLocationProvenanceEpoch = provenanceEpoch; } } private static bool TryGetRecordedLocationProvenanceEpoch(GameObject gameObject, out int provenanceEpoch) { provenanceEpoch = 0; if ((Object)(object)gameObject == (Object)null) { return false; } SpawnArea spawnArea = default(SpawnArea); if (gameObject.TryGetComponent(ref spawnArea) && ProvenanceRegistry.TryGetSpawnAreaProvenance(spawnArea, out SpawnerLocationProvenance provenance)) { provenanceEpoch = provenance.Epoch; return provenanceEpoch != 0; } CreatureSpawner creatureSpawner = default(CreatureSpawner); if (gameObject.TryGetComponent(ref creatureSpawner) && ProvenanceRegistry.TryGetCreatureSpawnerProvenance(creatureSpawner, out SpawnerLocationProvenance provenance2)) { provenanceEpoch = provenance2.Epoch; return provenanceEpoch != 0; } return false; } private static bool IsMatchingEntryCacheValid(GameObject gameObject, string configPrefabName, MatchingEntryCache? entryCache) { if ((Object)(object)gameObject == (Object)null || entryCache == null) { return false; } if (!string.Equals(entryCache.ConfigPrefabName, configPrefabName, StringComparison.OrdinalIgnoreCase)) { return false; } if (!entryCache.UsesLocationSelector) { return true; } if (TryGetRecordedLocationProvenanceEpoch(gameObject, out var provenanceEpoch)) { if (entryCache.HasRecordedLocationProvenanceEpoch) { return entryCache.RecordedLocationProvenanceEpoch == provenanceEpoch; } return false; } if (entryCache.HasRecordedLocationProvenanceEpoch) { return false; } return string.Equals(entryCache.ResolvedLocationKey, BuildSelectorLocationCacheKey(gameObject), StringComparison.OrdinalIgnoreCase); } private static string BuildSelectorLocationCacheKey(GameObject gameObject) { if (!TryGetLiveLocationContextForSelector(gameObject, out string locationPrefab)) { return ""; } return NormalizeSelectorLocationCacheKey(locationPrefab); } private static string NormalizeSelectorLocationCacheKey(string? locationPrefab) { string text = (locationPrefab ?? "").Trim(); if (text.Length <= 0) { return ""; } return text; } private static void AccumulateRuntimeConditionUsage(MatchingEntryCache cache, ConditionsDefinition? conditions) { if (conditions != null) { AccumulateRuntimeConditionUsage(cache.MutableRuntimeRequiredGlobalKeys, cache.MutableRuntimeForbiddenGlobalKeys, conditions, out var usesTimeOfDay, out var usesRequiredEnvironments, out var usesInsidePlayerBase); cache.UsesTimeOfDay |= usesTimeOfDay; cache.UsesRequiredEnvironments |= usesRequiredEnvironments; cache.UsesInsidePlayerBase |= usesInsidePlayerBase; } } private static void AccumulateRuntimeConditionUsage(List runtimeRequiredGlobalKeys, List runtimeForbiddenGlobalKeys, ConditionsDefinition? conditions, out bool usesTimeOfDay, out bool usesRequiredEnvironments, out bool usesInsidePlayerBase) { usesTimeOfDay = false; usesRequiredEnvironments = false; usesInsidePlayerBase = false; if (conditions != null) { usesTimeOfDay = conditions.TimeOfDay != null; usesRequiredEnvironments = HasConfiguredValues(conditions.RequiredEnvironments); usesInsidePlayerBase = conditions.InsidePlayerBase.HasValue; AddNormalizedConditionValues(runtimeRequiredGlobalKeys, conditions.RequiredGlobalKeys); AddNormalizedConditionValues(runtimeForbiddenGlobalKeys, conditions.ForbiddenGlobalKeys); } } private static bool HasConfiguredValues(List? values) { return values?.Any((string value) => !string.IsNullOrWhiteSpace(value)) ?? false; } private static void AddNormalizedConditionValues(List target, List? values) { if (values == null) { return; } foreach (string value in values) { string text = (value ?? "").Trim(); if (text.Length != 0 && !target.Contains(text, StringComparer.OrdinalIgnoreCase)) { target.Add(text); } } } private static bool HasRuntimeEntryConditions(SpawnerRuntimeEntry? entry) { if (entry != null) { return DropConditionEvaluator.HasConditions(entry.Conditions); } return false; } private static bool TrySelectWinningSpawnerEntryForRuntime(GameObject gameObject, MatchingEntryCache entryCache, bool forSpawnArea, RuntimeContextSnapshot runtimeContext, LocalRuntimeState localRuntimeState, int runtimeSignature, out SpawnerRuntimeEntry? winningEntry) { if (entryCache.WinningEntriesByRuntimeSignature.TryGetValue(runtimeSignature, out winningEntry)) { return winningEntry != null; } winningEntry = null; int num = -1; foreach (SpawnerRuntimeEntry entry in entryCache.Entries) { if (!entry.RuntimeReconcile || AreRuntimeSpawnerConditionsSatisfied(gameObject, entry.Conditions, runtimeContext, localRuntimeState)) { int num2 = (HasLocationSelector(entry) ? 1 : 0); if (num2 >= num) { winningEntry = entry; num = num2; } } } if (entryCache.WinningEntriesByRuntimeSignature.Count >= 16) { entryCache.WinningEntriesByRuntimeSignature.Clear(); } entryCache.WinningEntriesByRuntimeSignature[runtimeSignature] = winningEntry; return winningEntry != null; } private static bool TrySelectWinningSpawnerEntry(GameObject gameObject, IEnumerable? entries, bool forSpawnArea, out SpawnerRuntimeEntry? winningEntry) { winningEntry = null; if ((Object)(object)gameObject == (Object)null || entries == null) { return false; } bool flag = false; bool flag2 = false; string locationPrefab = ""; int num = -1; foreach (SpawnerRuntimeEntry entry in entries) { if (entry == null) { continue; } if (forSpawnArea) { if (entry.SpawnArea == null || !HasSpawnAreaOverride(entry.SpawnArea)) { continue; } } else if (entry.CreatureSpawner == null || !HasCreatureSpawnerOverride(entry.CreatureSpawner)) { continue; } if (!flag && HasLocationSelector(entry)) { flag2 = TryGetLiveLocationContextForSelector(gameObject, out locationPrefab); flag = true; } if (TryGetLocationMatchSpecificity(gameObject, entry.Locations, flag2, flag2 ? locationPrefab : null, out var specificity) && (!HasRuntimeEntryConditions(entry) || DropConditionEvaluator.AreSatisfied(gameObject, entry.Conditions)) && specificity >= num) { winningEntry = entry; num = specificity; } } return winningEntry != null; } private static bool TryGetLocationMatchSpecificity(GameObject gameObject, List? locations, bool hasResolvedLocation, string? resolvedLocationPrefab, out int specificity) { specificity = 0; if ((Object)(object)gameObject == (Object)null) { return false; } if (!HasLocationSelector(locations)) { return true; } if (!hasResolvedLocation) { return false; } if (!MatchesLocationSelector(locations, resolvedLocationPrefab)) { return false; } specificity = 1; return true; } private static bool TryGetLiveLocationContextForSelector(GameObject gameObject, out string locationPrefab) { locationPrefab = ""; if ((Object)(object)gameObject == (Object)null) { return false; } if (TryGetRecordedLocationContext(gameObject, out locationPrefab, out string relativePath)) { return locationPrefab.Length > 0; } if (TryGetActiveLocationSpawnContextPrefab(out locationPrefab)) { return locationPrefab.Length > 0; } if (TryGetLiveLocationProxyPrefab(gameObject, out locationPrefab)) { return locationPrefab.Length > 0; } if (TryGetClonedZoneLocationContext(gameObject, out locationPrefab)) { return true; } if (TryGetPersistedSpawnerLocationContext(gameObject, out locationPrefab, out relativePath)) { return true; } if (TryGetDungeonGeneratorLocationContext(gameObject, out locationPrefab)) { return true; } if (TryGetDirectLocationContext(gameObject, out locationPrefab, out relativePath)) { return locationPrefab.Length > 0; } if (TryGetStaticLocationContext(gameObject, out locationPrefab, out relativePath)) { return locationPrefab.Length > 0; } if (TryGetZoneLocationContext(gameObject, out locationPrefab)) { return locationPrefab.Length > 0; } if (TryPromoteSpatialContextToRecordedProvenance(gameObject, out locationPrefab, out relativePath)) { return locationPrefab.Length > 0; } return false; } private static bool TryGetLiveLocationProxyPrefab(GameObject gameObject, out string locationPrefab) { locationPrefab = ""; if ((Object)(object)gameObject == (Object)null) { return false; } Transform locationContextRootTransform = GetLocationContextRootTransform(gameObject); if ((Object)(object)locationContextRootTransform == (Object)null) { return false; } return ProvenanceRegistry.TryGetRecordedRootLocationPrefab(locationContextRootTransform, out locationPrefab); } private static int ClampSpawnAreaMaxTotalSpawns(int value) { return SpawnerGlobalConfig.ClampSpawnAreaMaxTotalSpawns(value); } private static int ResolveSpawnAreaMaxTotalSpawns(int? configuredMaxTotalSpawns) { if (!configuredMaxTotalSpawns.HasValue) { return PluginSettingsFacade.GetDefaultSpawnAreaMaxTotalSpawns(); } return ClampSpawnAreaMaxTotalSpawns(configuredMaxTotalSpawns.Value); } private static bool ApplySpawnAreaTotalSpawnLimit(SpawnArea? spawnArea, int? configuredMaxTotalSpawns) { if ((Object)(object)spawnArea == (Object)null) { return true; } bool hasValue = configuredMaxTotalSpawns.HasValue; int num = ResolveSpawnAreaMaxTotalSpawns(configuredMaxTotalSpawns); if (num <= 0 && !hasValue) { ClearAppliedSpawnAreaTotalSpawnLimit(spawnArea); return true; } SpawnAreaTotalSpawnLimitState state = new SpawnAreaTotalSpawnLimitState(num, hasValue); SpawnAreaTotalSpawnLimitState state2; bool num2 = !LiveReconcilerState.TryGetAppliedSpawnAreaTotalSpawnLimit(spawnArea, out state2) || state2.MaxTotalSpawns != state.MaxTotalSpawns || state2.FromYamlOverride != state.FromYamlOverride; LiveReconcilerState.SetAppliedSpawnAreaTotalSpawnLimit(spawnArea, state); if (num2 && TryGetSpawnAreaZdo(spawnArea, out ZDO zdo)) { zdo.Set(SpawnAreaMaxTotalSpawnsZdoKey, num, false); } return !DestroySpawnAreaIfTotalSpawnLimitExhausted(spawnArea, num); } private static bool PrepareSpawnAreaTotalSpawnLimit(SpawnArea? spawnArea) { if ((Object)(object)spawnArea == (Object)null) { return true; } if (LiveReconcilerState.TryGetAppliedSpawnAreaTotalSpawnLimit(spawnArea, out var state) && state.FromYamlOverride) { return !DestroySpawnAreaIfTotalSpawnLimitExhausted(spawnArea, state.MaxTotalSpawns); } return ApplySpawnAreaTotalSpawnLimit(spawnArea, null); } private static void RecordSuccessfulSpawnAreaTotalSpawn(SpawnArea? spawnArea) { if (!((Object)(object)spawnArea == (Object)null) && PrepareSpawnAreaTotalSpawnLimit(spawnArea) && LiveReconcilerState.TryGetAppliedSpawnAreaTotalSpawnLimit(spawnArea, out var state) && state.MaxTotalSpawns > 0 && TryGetSpawnAreaZdo(spawnArea, out ZDO zdo)) { int num = Mathf.Max(0, zdo.GetInt(SpawnAreaTotalSpawnCountZdoKey, 0)) + 1; zdo.Set(SpawnAreaTotalSpawnCountZdoKey, num, false); zdo.Set(SpawnAreaMaxTotalSpawnsZdoKey, state.MaxTotalSpawns, false); if (num >= state.MaxTotalSpawns) { DestroySpawnAreaForTotalSpawnLimit(spawnArea); } } } private static bool DestroySpawnAreaIfTotalSpawnLimitExhausted(SpawnArea? spawnArea, int maxTotalSpawns) { if ((Object)(object)spawnArea == (Object)null || maxTotalSpawns <= 0 || !TryGetSpawnAreaZdo(spawnArea, out ZDO zdo)) { return false; } if (Mathf.Max(0, zdo.GetInt(SpawnAreaTotalSpawnCountZdoKey, 0)) < maxTotalSpawns) { return false; } return DestroySpawnAreaForTotalSpawnLimit(spawnArea); } private static bool DestroySpawnAreaForTotalSpawnLimit(SpawnArea? spawnArea) { if ((Object)(object)spawnArea == (Object)null || (Object)(object)((Component)spawnArea).gameObject == (Object)null) { return false; } ZNetView component = ((Component)spawnArea).GetComponent(); if ((Object)(object)component == (Object)null || !component.IsValid() || !component.IsOwner()) { return false; } Destructible component2 = ((Component)spawnArea).GetComponent(); if ((Object)(object)component2 != (Object)null) { component2.Destroy((HitData)null); return true; } component.Destroy(); return true; } private static bool TryGetSpawnAreaZdo(SpawnArea? spawnArea, out ZDO zdo) { zdo = null; if ((Object)(object)spawnArea == (Object)null) { return false; } ZNetView component = ((Component)spawnArea).GetComponent(); if ((Object)(object)component == (Object)null || !component.IsValid()) { return false; } ZDO zDO = component.GetZDO(); if (zDO == null) { return false; } zdo = zDO; return true; } private static void ClearAppliedSpawnAreaTotalSpawnLimit(SpawnArea? spawnArea) { LiveReconcilerState.RemoveAppliedSpawnAreaTotalSpawnLimit(spawnArea); } private static string BuildPrimaryOverrideConfigurationTemplate() { StringBuilder stringBuilder = new StringBuilder(); CommentedYamlTemplateSupport.AppendTemplateComment(stringBuilder, "Any file named " + PluginSettingsFacade.GetYamlDomainSupplementalPrefix("spawner") + "*.yml or " + PluginSettingsFacade.GetYamlDomainSupplementalPrefix("spawner") + "*.yaml is also loaded."); CommentedYamlTemplateSupport.AppendTemplateComment(stringBuilder, "Use " + PluginSettingsFacade.GetYamlDomainFilePrefix("spawner") + ".reference.yml to look up real spawner object names and " + PluginSettingsFacade.GetYamlDomainFilePrefix("spawner") + ".locations.reference.yml to look up valid location names"); CommentedYamlTemplateSupport.AppendTemplateComment(stringBuilder, "Run `dns:full spawner` to regenerate " + PluginSettingsFacade.GetYamlDomainFilePrefix("spawner") + ".full.yml for exhaustive field examples"); CommentedYamlTemplateSupport.AppendTemplateComment(stringBuilder, "Only the most specific passing entry is applied # Less specific entries act as fallback # If multiple passing entries share the same specificity, the later loaded one wins"); CommentedYamlTemplateSupport.AppendTemplateBlankLine(stringBuilder); CommentedYamlTemplateSupport.AppendTemplateComment(stringBuilder, "spawnArea"); CommentedYamlTemplateSupport.AppendTemplateBlankLine(stringBuilder); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 0, "- prefab: BonePileSpawner"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 1, "enabled: true"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 1, "locations: [Grave1] # ex) locations[] from " + PluginSettingsFacade.GetYamlDomainFilePrefix("spawner") + ".locations.reference.yml # Optional"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 1, "conditions: # If these conditions fail, this custom entry is ignored and the original spawner behavior is used"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 2, "altitude: null # ex) -1000~1000 # Range in world-height meters"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 2, "distanceFromCenter: null # ex) 0~10000 # Range in meters from the world center"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 2, "biomes: [] # ex) [BlackForest, Mistlands] # Allowed biomes # EWD custom biome names also work when EWD is installed"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 2, "timeOfDay: null # ex) [night] # [day, night]"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 2, "requiredEnvironments: [] # ex) [Rain] # Allowed environment names"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 2, "requiredGlobalKeys: [] # ex) [defeated_gdking] # Required global keys"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 2, "forbiddenGlobalKeys: [] # ex) [nomap] # Forbidden global keys"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 2, "inForest: null # ex) true = forest only # false = outside forest only # null or no field allows both"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 2, "inDungeon: null # ex) true = dungeon only # false = overworld only # null or no field allows both"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 2, "insidePlayerBase: null # ex) true = near player base only # false = away from player base only # null or no field allows both # Valid for spawnArea-only entry gating"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 1, "spawnArea:"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 2, "levelUpChance: 15 # Percent chance for each extra level roll when creature level is a range"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 2, "spawnInterval: 30 # Seconds between spawn attempts after the timer passes"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 2, "triggerDistance: 256 # Meters from the spawner within which players must be present for spawning to tick"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 2, "setPatrolSpawnPoint: true # True gives spawned AI this spawner as its patrol or home point"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 2, "spawnRadius: 2 # Meters around the SpawnArea center used for spawn placement"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 2, "nearRadius: 10 # Meters used by the native near-count cap"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 2, "farRadius: 1000 # Meters used by the native total-count cap"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 2, "maxNear: 3 # Native cap on living creatures inside nearRadius"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 2, "maxTotal: 20 # Native cap on living creatures inside farRadius"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 2, "maxTotalSpawns: null # Optional total successful-spawn limit for this SpawnArea # null uses General / Default SpawnArea Max Total Spawns # 0 disables # 1~1000 destroys after that many successful spawns"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 2, "onGroundOnly: false # True restricts spawn placement to grounded points only"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 2, "creatures:"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 2, "- creature: null # ex) Draugr # Required creature prefab name"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 3, "weight: 1 # Relative weight versus other entries in the same SpawnArea list"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 3, "level: 1~1 # ex) 1~2 # Range of spawned creature levels"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 3, "data: null # Optional Expand World Data entry applied to the spawned creature"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 3, "fields: {} # ex) { Character.m_name: $enemy_draugr, health: 200 } # Expand World Data field overrides layered on top of data"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 3, "objects: [] # ex) [Wood,0,0,0,1] # Expand World Data object entries spawned at the final spawn point"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 3, "faction: null # ex) Boss # Values: " + FactionIntegration.GetNativeFactionList()); CommentedYamlTemplateSupport.AppendTemplateBlankLine(stringBuilder); CommentedYamlTemplateSupport.AppendTemplateComment(stringBuilder, "creatureSpawner"); CommentedYamlTemplateSupport.AppendTemplateBlankLine(stringBuilder); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 0, "- prefab: Spawner_Boar"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 1, "enabled: true"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 1, "locations: [Runestone_Boars] # ex) locations[] from " + PluginSettingsFacade.GetYamlDomainFilePrefix("spawner") + ".locations.reference.yml # Optional"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 1, "conditions:"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 2, "altitude: null"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 2, "distanceFromCenter: null"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 2, "biomes: []"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 2, "requiredEnvironments: []"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 2, "inForest: null"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 2, "inDungeon: null"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 1, "creatureSpawner:"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 2, "creature: null # ex) Skeleton # Required creature prefab name"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 2, "timeOfDay: [day, night] # Runtime gate for this spawner"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 2, "requiredGlobalKey: '' # ex) defeated_gdking # Use '' to clear the native requirement # Omit the key to keep the current value"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 2, "blockingGlobalKey: '' # ex) nomap # Use '' to clear the native block key # Omit the key to keep the current value"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 2, "level: 1~1 # ex) 1~2 # Range of spawned creature levels"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 2, "levelUpChance: 10 # Percent chance for each extra level roll when level is a range"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 2, "respawnTimeMinutes: 20 # 0 disables respawn after the first successful spawn # Minutes before this spawner can respawn after the previous creature is gone"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 2, "spawnCheckInterval: 5 # Seconds between UpdateSpawner checks for this spawner"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 2, "spawnGroupId: 0 # Use spawnGroupRadius: 0 to avoid grouping # Nearby spawners with the same id can share native group blocking"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 2, "spawnGroupRadius: 0 # Meters used to link nearby same-id spawners into one native group"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 2, "spawnerWeight: 1 # Relative weight when a native spawn group chooses one spawner to fire"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 2, "maxGroupSpawned: 1 # Max living creatures allowed across the native spawn group"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 2, "triggerDistance: 60 # Meters from this spawner within which players must be present for UpdateSpawner to pass"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 2, "triggerNoise: 0 # 0 ignores noise # Meters-equivalent native noise threshold required inside triggerDistance"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 2, "allowInsidePlayerBase: false # True allows spawning inside player-base suppression areas"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 2, "wakeUpAnimation: false # True plays the wake-up animation when the creature appears"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 2, "setPatrolSpawnPoint: false # True makes spawned AI patrol around this spawner"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 2, "faction: null"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 2, "data: null"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 2, "fields: {}"); CommentedYamlTemplateSupport.AppendTemplateLine(stringBuilder, 2, "objects: []"); CommentedYamlTemplateSupport.AppendTemplateBlankLine(stringBuilder); return stringBuilder.ToString(); } private static void RefreshVneiCompatibility(Dictionary previousEntrySignatures) { RefreshVneiCompatibility(previousEntrySignatures, CloneCurrentEntrySignaturesByPrefab()); } private static void RefreshVneiCompatibility(Dictionary previousEntrySignatures, Dictionary currentEntrySignatures) { VneiCompatibility.RefreshSpawnerPrefabs(BuildDirtyPrefabs(previousEntrySignatures, currentEntrySignatures)); } internal static bool HasVneiRelevantSpawnAreaEntries(string prefabName) { lock (Sync) { List value; return RuntimeState.ActiveEntriesByPrefab.TryGetValue(prefabName ?? "", out value) && value.Any((SpawnerConfigurationEntry entry) => entry.SpawnArea?.Creatures != null); } } internal static bool TryGetVneiDisplayForSpawnArea(SpawnArea spawnArea, out List results) { lock (Sync) { results = new List(); if ((Object)(object)spawnArea == (Object)null) { return false; } string configPrefabName = GetConfigPrefabName(((Component)spawnArea).gameObject, "SpawnArea"); RuntimeState.ActiveEntriesByPrefab.TryGetValue(configPrefabName, out List value); SpawnerConfigurationEntry spawnerConfigurationEntry = null; IEnumerable enumerable = value; foreach (SpawnerConfigurationEntry item in enumerable ?? Enumerable.Empty()) { if (item.SpawnArea?.Creatures != null && !DropConditionEvaluator.HasConditions(item.Conditions) && !HasLocationSelector(item)) { spawnerConfigurationEntry = item; } } Dictionary dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); if (spawnerConfigurationEntry?.SpawnArea?.Creatures != null) { AddVneiSpawnAreaWeights(dictionary, spawnerConfigurationEntry.SpawnArea.Creatures); } else { AddVneiSpawnAreaWeights(dictionary, spawnArea.m_prefabs); } enumerable = value; foreach (SpawnerConfigurationEntry item2 in enumerable ?? Enumerable.Empty()) { if (item2 != spawnerConfigurationEntry && item2.SpawnArea?.Creatures != null) { AddVneiSpawnAreaWeights(dictionary, item2.SpawnArea.Creatures); } } if (dictionary.Count == 0) { return true; } float num = dictionary.Values.Sum((float weight) => Mathf.Max(0f, weight)); float num2 = ((dictionary.Count == 0) ? 1f : (1f / (float)dictionary.Count)); foreach (KeyValuePair item3 in dictionary.OrderBy, string>((KeyValuePair pair) => pair.Key, StringComparer.OrdinalIgnoreCase)) { item3.Deconstruct(out var key, out var value2); string prefabName = key; float num3 = value2; float chance = ((num <= 0f) ? num2 : Mathf.Clamp01(Mathf.Max(0f, num3) / num)); results.Add(new VneiRecipeResult(prefabName, 1, 1, 1f, 1, 1, chance)); } return dictionary.Count > 0 || (value?.Count ?? 0) > 0 || spawnArea.m_prefabs.Count > 0; } } private static void AddVneiSpawnAreaWeights(Dictionary weightsByPrefab, IEnumerable? prefabs) { foreach (SpawnData item in prefabs ?? Enumerable.Empty()) { if (!((Object)(object)item?.m_prefab == (Object)null)) { string name = ((Object)item.m_prefab).name; if (!weightsByPrefab.TryGetValue(name, out var value)) { value = 0f; } weightsByPrefab[name] = value + Mathf.Max(0f, item.m_weight); } } } private static void AddVneiSpawnAreaWeights(Dictionary weightsByPrefab, IEnumerable? definitions) { foreach (SpawnAreaSpawnDefinition item in definitions ?? Enumerable.Empty()) { string text = (item.Creature ?? "").Trim(); if (text.Length != 0) { if (!weightsByPrefab.TryGetValue(text, out var value)) { value = 0f; } weightsByPrefab[text] = value + Mathf.Max(0f, item.Weight ?? 1f); } } } } internal static class VneiCompatibility { private enum ManagedRecipeKind { CharacterDrop, Container, DropOnDestroyed, MineRock, MineRock5, Pickable, Fish, SpawnArea, Destructible, TreeBase } private readonly struct ManagedRecipeKey : IEquatable { public string PrefabName { get; } public ManagedRecipeKind Kind { get; } public ManagedRecipeKey(string prefabName, ManagedRecipeKind kind) { PrefabName = prefabName ?? ""; Kind = kind; } public bool Equals(ManagedRecipeKey other) { if (Kind == other.Kind) { return string.Equals(PrefabName, other.PrefabName, StringComparison.OrdinalIgnoreCase); } return false; } public override bool Equals(object? obj) { if (obj is ManagedRecipeKey other) { return Equals(other); } return false; } public override int GetHashCode() { return (StringComparer.OrdinalIgnoreCase.GetHashCode(PrefabName) * 397) ^ (int)Kind; } } private sealed class ManagedRecipeBinding { public object Recipe { get; set; } public Object? Source { get; set; } public bool IsSupplemental { get; set; } public bool DisableSourceItemWhenRemoved { get; set; } public string LastDisplayFingerprint { get; set; } = ""; } private const string VneiPluginGuid = "com.maxsch.valheim.vnei"; private const double RefreshFrameBudgetSeconds = 0.0015; private const int MaxRefreshJobsPerFrame = 8; private static bool _initialized; private static bool _available; private static Type? _recipeInfoType; private static Type? _indexingType; private static Type? _amountType; private static Type? _itemType; private static Type? _partType; private static ConstructorInfo? _emptyRecipeCtor; private static ConstructorInfo? _rangeAmountCtor; private static ConstructorInfo? _singleAmountCtor; private static MethodInfo? _addIngredientMethod; private static MethodInfo? _addResultMethod; private static MethodInfo? _addRecipeToItemsMethod; private static MethodInfo? _getItemMethod; private static MethodInfo? _hasIndexedMethod; private static MethodInfo? _combineGroupAmountsMethod; private static MethodInfo? _recipeCalculateIsOnBlacklistMethod; private static MethodInfo? _recipeCalculateWidthMethod; private static MethodInfo? _recipeUpdateKnownMethod; private static MethodInfo? _itemUpdateKnownMethod; private static PropertyInfo? _ingredientsProperty; private static PropertyInfo? _resultsProperty; private static PropertyInfo? _stationsProperty; private static PropertyInfo? _recipesProperty; private static FieldInfo? _itemIsActiveField; private static FieldInfo? _itemResultField; private static FieldInfo? _itemIngredientField; private static FieldInfo? _partItemField; private static FieldInfo? _recipeIsOnBlacklistBackingField; private static readonly Dictionary ManagedRecipesByKey = new Dictionary(); private static readonly object RefreshSync = new object(); private static readonly RingBufferQueue PendingRefreshWorkItems = new RingBufferQueue(); private static readonly HashSet PendingRefreshWorkSet = new HashSet(); private static Coroutine? _pendingRefreshCoroutine; internal static void Initialize(Harmony harmony) { if (_initialized) { return; } _initialized = true; if (!Chainloader.PluginInfos.ContainsKey("com.maxsch.valheim.vnei")) { return; } _recipeInfoType = AccessTools.TypeByName("VNEI.Logic.RecipeInfo"); _indexingType = AccessTools.TypeByName("VNEI.Logic.Indexing"); _amountType = AccessTools.TypeByName("VNEI.Logic.Amount"); _itemType = AccessTools.TypeByName("VNEI.Logic.Item"); _partType = AccessTools.TypeByName("VNEI.Logic.Part"); if (_recipeInfoType == null || _indexingType == null || _amountType == null || _itemType == null || _partType == null) { DropNSpawnPlugin.DropNSpawnLogger.LogWarning((object)"VNEI detected, but one or more compatibility types could not be resolved."); return; } _emptyRecipeCtor = AccessTools.Constructor(_recipeInfoType, Type.EmptyTypes, false); _rangeAmountCtor = AccessTools.Constructor(_amountType, new Type[3] { typeof(int), typeof(int), typeof(float) }, false); _singleAmountCtor = AccessTools.Constructor(_amountType, new Type[2] { typeof(int), typeof(float) }, false); _addIngredientMethod = AccessTools.Method(_recipeInfoType, "AddIngredient", new Type[4] { typeof(string), _amountType, _amountType, typeof(int) }, (Type[])null); _addResultMethod = AccessTools.Method(_recipeInfoType, "AddResult", new Type[4] { typeof(string), _amountType, _amountType, typeof(int) }, (Type[])null); _addRecipeToItemsMethod = AccessTools.Method(_indexingType, "AddRecipeToItems", new Type[1] { _recipeInfoType }, (Type[])null); _getItemMethod = AccessTools.Method(_indexingType, "GetItem", new Type[1] { typeof(string) }, (Type[])null); _hasIndexedMethod = AccessTools.Method(_indexingType, "HasIndexed", Type.EmptyTypes, (Type[])null); _combineGroupAmountsMethod = AccessTools.Method(_recipeInfoType, "CombineGroupAmounts", new Type[1] { typeof(Dictionary<, >).MakeGenericType(_amountType, typeof(List<>).MakeGenericType(_partType)) }, (Type[])null); _recipeCalculateIsOnBlacklistMethod = AccessTools.Method(_recipeInfoType, "CalculateIsOnBlacklist", Type.EmptyTypes, (Type[])null); _recipeCalculateWidthMethod = AccessTools.Method(_recipeInfoType, "CalculateWidth", Type.EmptyTypes, (Type[])null); _recipeUpdateKnownMethod = AccessTools.Method(_recipeInfoType, "UpdateKnown", Type.EmptyTypes, (Type[])null); _itemUpdateKnownMethod = AccessTools.Method(_itemType, "UpdateKnown", Type.EmptyTypes, (Type[])null); _ingredientsProperty = AccessTools.Property(_recipeInfoType, "Ingredients"); _resultsProperty = AccessTools.Property(_recipeInfoType, "Results"); _stationsProperty = AccessTools.Property(_recipeInfoType, "Stations"); _recipesProperty = AccessTools.Property(_recipeInfoType, "Recipes"); _itemIsActiveField = AccessTools.Field(_itemType, "isActive"); _itemResultField = AccessTools.Field(_itemType, "result"); _itemIngredientField = AccessTools.Field(_itemType, "ingredient"); _partItemField = AccessTools.Field(_partType, "item"); _recipeIsOnBlacklistBackingField = AccessTools.Field(_recipeInfoType, "k__BackingField"); _available = _emptyRecipeCtor != null && _rangeAmountCtor != null && _singleAmountCtor != null && _addIngredientMethod != null && _addResultMethod != null && _addRecipeToItemsMethod != null && _getItemMethod != null && _hasIndexedMethod != null && _combineGroupAmountsMethod != null && _recipeCalculateIsOnBlacklistMethod != null && _recipeCalculateWidthMethod != null && _recipeUpdateKnownMethod != null && _itemUpdateKnownMethod != null && _ingredientsProperty != null && _resultsProperty != null && _stationsProperty != null && _recipesProperty != null && _itemResultField != null && _itemIngredientField != null && _partItemField != null; if (!_available) { DropNSpawnPlugin.DropNSpawnLogger.LogWarning((object)"VNEI detected, but one or more compatibility members could not be resolved."); return; } PatchConstructor(harmony, new Type[1] { typeof(CharacterDrop) }, "CharacterDropCtorPostfix"); PatchConstructor(harmony, new Type[2] { typeof(GameObject), typeof(DropTable) }, "DropTableCtorPostfix"); PatchConstructor(harmony, new Type[2] { typeof(GameObject), typeof(Pickable) }, "PickableCtorPostfix"); PatchConstructor(harmony, new Type[1] { typeof(SpawnArea) }, "SpawnAreaCtorPostfix"); PatchConstructor(harmony, new Type[1] { typeof(Destructible) }, "DestructibleCtorPostfix"); PatchConstructor(harmony, new Type[1] { typeof(TreeBase) }, "TreeBaseCtorPostfix"); System.Reflection.EventInfo eventInfo = _indexingType.GetEvent("OnIndexingItemRecipes", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); if (eventInfo != null) { eventInfo.AddEventHandler(null, new Action(HandleIndexingItemRecipes)); } DropNSpawnPlugin.DropNSpawnLogger.LogInfo((object)"VNEI compatibility enabled."); } internal static void RefreshCharacterPrefabs(IEnumerable prefabNames) { EnqueueRefresh(from prefabName in NormalizePrefabNames(prefabNames) select new ManagedRecipeKey(prefabName, ManagedRecipeKind.CharacterDrop)); } internal static void RefreshObjectPrefabs(IEnumerable prefabNames) { EnqueueRefresh(ExpandObjectPrefabNames(prefabNames).SelectMany(BuildObjectRefreshWorkItems)); } internal static void RefreshSpawnerPrefabs(IEnumerable prefabNames) { EnqueueRefresh(from prefabName in NormalizePrefabNames(prefabNames) select new ManagedRecipeKey(prefabName, ManagedRecipeKind.SpawnArea)); } private static bool CanRefresh() { if (_available) { return HasIndexed(); } return false; } private static bool HasIndexed() { try { return _hasIndexedMethod != null && (bool)(_hasIndexedMethod.Invoke(null, Array.Empty()) ?? ((object)false)); } catch { return false; } } private static IEnumerable NormalizePrefabNames(IEnumerable prefabNames) { return (from name in prefabNames ?? Array.Empty() select (name ?? "").Trim() into name where name.Length > 0 select name).Distinct(StringComparer.OrdinalIgnoreCase); } private static HashSet ExpandObjectPrefabNames(IEnumerable prefabNames) { HashSet hashSet = NormalizePrefabNames(prefabNames).ToHashSet(StringComparer.OrdinalIgnoreCase); if (hashSet.Count == 0 || (Object)(object)ZNetScene.instance == (Object)null) { return hashSet; } HashSet hashSet2 = new HashSet(ZNetScene.instance.m_prefabs); hashSet2.UnionWith(ZNetScene.instance.m_namedPrefabs.Values); TreeBase val = default(TreeBase); foreach (GameObject item in hashSet2) { if (!((Object)(object)item == (Object)null) && !hashSet.Contains(((Object)item).name) && item.TryGetComponent(ref val) && TreeBaseUsesAnyChangedLog(val.m_logPrefab, hashSet, 0)) { hashSet.Add(((Object)item).name); } } return hashSet; } private static bool TreeBaseUsesAnyChangedLog(GameObject? logPrefab, HashSet changedPrefabs, int depth) { if ((Object)(object)logPrefab == (Object)null || depth > 50) { return false; } if (changedPrefabs.Contains(((Object)logPrefab).name)) { return true; } TreeLog val = default(TreeLog); if (!logPrefab.TryGetComponent(ref val) || val.m_subLogPoints == null || val.m_subLogPoints.Length == 0) { return false; } return TreeBaseUsesAnyChangedLog(val.m_subLogPrefab, changedPrefabs, depth + 1); } private static IEnumerable BuildObjectRefreshWorkItems(string prefabName) { yield return new ManagedRecipeKey(prefabName, ManagedRecipeKind.Container); yield return new ManagedRecipeKey(prefabName, ManagedRecipeKind.DropOnDestroyed); yield return new ManagedRecipeKey(prefabName, ManagedRecipeKind.MineRock); yield return new ManagedRecipeKey(prefabName, ManagedRecipeKind.MineRock5); yield return new ManagedRecipeKey(prefabName, ManagedRecipeKind.Pickable); yield return new ManagedRecipeKey(prefabName, ManagedRecipeKind.Fish); yield return new ManagedRecipeKey(prefabName, ManagedRecipeKind.Destructible); yield return new ManagedRecipeKey(prefabName, ManagedRecipeKind.TreeBase); } private static void EnqueueRefresh(IEnumerable workItems) { if (!_available) { return; } bool flag = false; lock (RefreshSync) { foreach (ManagedRecipeKey workItem in workItems) { if (PendingRefreshWorkSet.Add(workItem)) { PendingRefreshWorkItems.Enqueue(workItem); } } if (_pendingRefreshCoroutine == null && (Object)(object)DropNSpawnPlugin.Instance != (Object)null && PendingRefreshWorkItems.Count > 0) { flag = true; _pendingRefreshCoroutine = ((MonoBehaviour)DropNSpawnPlugin.Instance).StartCoroutine(ProcessQueuedRefreshesCoroutine()); } } if (!flag && (Object)(object)DropNSpawnPlugin.Instance == (Object)null) { ProcessQueuedRefreshes(int.MaxValue, double.PositiveInfinity); } } private static IEnumerator ProcessQueuedRefreshesCoroutine() { yield return null; while (true) { if (CanRefresh()) { ProcessQueuedRefreshes(8, Time.realtimeSinceStartupAsDouble + 0.0015); } lock (RefreshSync) { if (PendingRefreshWorkItems.Count == 0) { _pendingRefreshCoroutine = null; break; } } yield return null; } } private static bool ProcessQueuedRefreshes(int maxJobs, double deadline) { if (!CanRefresh()) { return false; } int i = 0; HashSet hashSet = null; for (; i < maxJobs; i++) { if (!(Time.realtimeSinceStartupAsDouble < deadline)) { break; } if (!TryDequeueRefreshWorkItem(out var workItem)) { break; } if (hashSet == null) { hashSet = new HashSet(); } ProcessQueuedRefreshWorkItem(workItem, hashSet); } if (hashSet != null && hashSet.Count > 0) { UpdateKnownForItems(hashSet); } return i > 0; } private static bool TryDequeueRefreshWorkItem(out ManagedRecipeKey workItem) { lock (RefreshSync) { if (!PendingRefreshWorkItems.TryDequeue(out workItem)) { return false; } PendingRefreshWorkSet.Remove(workItem); return true; } } private static void ProcessQueuedRefreshWorkItem(ManagedRecipeKey workItem, HashSet affectedItems) { switch (workItem.Kind) { case ManagedRecipeKind.CharacterDrop: RefreshCharacterPrefab(workItem.PrefabName, affectedItems); break; case ManagedRecipeKind.Container: RefreshContainerPrefab(workItem.PrefabName, affectedItems); break; case ManagedRecipeKind.DropOnDestroyed: case ManagedRecipeKind.MineRock: case ManagedRecipeKind.MineRock5: RefreshDropTablePrefab(workItem.PrefabName, workItem.Kind, affectedItems); break; case ManagedRecipeKind.Pickable: RefreshPickablePrefab(workItem.PrefabName, affectedItems); break; case ManagedRecipeKind.Fish: RefreshFishPrefab(workItem.PrefabName, affectedItems); break; case ManagedRecipeKind.SpawnArea: RefreshSpawnAreaPrefab(workItem.PrefabName, affectedItems); break; case ManagedRecipeKind.Destructible: RefreshDestructiblePrefab(workItem.PrefabName, affectedItems); break; case ManagedRecipeKind.TreeBase: RefreshTreeBasePrefab(workItem.PrefabName, affectedItems); break; } } private static void PatchConstructor(Harmony harmony, Type[] argumentTypes, string postfixName) { //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Expected O, but got Unknown if (!(_recipeInfoType == null)) { ConstructorInfo constructorInfo = AccessTools.Constructor(_recipeInfoType, argumentTypes, false); MethodInfo methodInfo = AccessTools.Method(typeof(VneiCompatibility), postfixName, (Type[])null, (Type[])null); if (!(constructorInfo == null) && !(methodInfo == null)) { harmony.Patch((MethodBase)constructorInfo, (HarmonyMethod)null, new HarmonyMethod(methodInfo), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } } } private static void CharacterDropCtorPostfix(object __instance, [HarmonyArgument(0)] CharacterDrop characterDrop) { if (_available && !((Object)(object)characterDrop == (Object)null) && !((Object)(object)((Component)characterDrop).gameObject == (Object)null)) { ManagedRecipeBinding managedRecipeBinding = RegisterManagedRecipe(new ManagedRecipeKey(((Object)((Component)characterDrop).gameObject).name, ManagedRecipeKind.CharacterDrop), __instance, (Object?)(object)characterDrop, isSupplemental: false, disableSourceItemWhenRemoved: false); if (CharacterDropManager.HasVneiRelevantEntries(((Object)((Component)characterDrop).gameObject).name) && CharacterDropManager.TryGetVneiDisplayResults(characterDrop, out List results)) { RewriteRecipeResults(__instance, results); managedRecipeBinding.LastDisplayFingerprint = BuildResultsFingerprint(results); } } } private static void DropTableCtorPostfix(object __instance, [HarmonyArgument(0)] GameObject from, [HarmonyArgument(1)] DropTable dropTable) { if (_available && !((Object)(object)from == (Object)null) && dropTable != null && TryGetDropTableRecipeKey(from, dropTable, out var key)) { ManagedRecipeBinding managedRecipeBinding = RegisterManagedRecipe(key, __instance, (Object?)(object)from, isSupplemental: false, disableSourceItemWhenRemoved: false); if (ShouldRewriteDropTableRecipe(key) && ObjectDropManager.TryGetVneiDisplayForDropTable(from, dropTable, out List results)) { RewriteRecipeResults(__instance, results); managedRecipeBinding.LastDisplayFingerprint = BuildResultsFingerprint(results); } } } private static void PickableCtorPostfix(object __instance, [HarmonyArgument(0)] GameObject prefab, [HarmonyArgument(1)] Pickable pickable) { if (_available && !((Object)(object)prefab == (Object)null) && !((Object)(object)pickable == (Object)null)) { ManagedRecipeBinding managedRecipeBinding = RegisterManagedRecipe(new ManagedRecipeKey(((Object)prefab).name, ManagedRecipeKind.Pickable), __instance, (Object?)(object)pickable, isSupplemental: false, disableSourceItemWhenRemoved: false); if (ObjectDropManager.HasVneiRelevantPickableOverride(((Object)prefab).name) && ObjectDropManager.TryGetVneiDisplayForPickable(prefab, pickable, out List results)) { RewriteRecipeResults(__instance, results); managedRecipeBinding.LastDisplayFingerprint = BuildResultsFingerprint(results); } } } private static void SpawnAreaCtorPostfix(object __instance, [HarmonyArgument(0)] SpawnArea spawnArea) { if (_available && !((Object)(object)spawnArea == (Object)null) && !((Object)(object)((Component)spawnArea).gameObject == (Object)null)) { ManagedRecipeBinding managedRecipeBinding = RegisterManagedRecipe(new ManagedRecipeKey(((Object)((Component)spawnArea).gameObject).name, ManagedRecipeKind.SpawnArea), __instance, (Object?)(object)spawnArea, isSupplemental: false, disableSourceItemWhenRemoved: false); if (SpawnerManager.HasVneiRelevantSpawnAreaEntries(((Object)((Component)spawnArea).gameObject).name) && SpawnerManager.TryGetVneiDisplayForSpawnArea(spawnArea, out List results)) { RewriteRecipeResults(__instance, results); managedRecipeBinding.LastDisplayFingerprint = BuildResultsFingerprint(results); } } } private static void DestructibleCtorPostfix(object __instance, [HarmonyArgument(0)] Destructible destructible) { if (_available && !((Object)(object)destructible == (Object)null) && !((Object)(object)((Component)destructible).gameObject == (Object)null)) { ManagedRecipeBinding managedRecipeBinding = RegisterManagedRecipe(new ManagedRecipeKey(((Object)((Component)destructible).gameObject).name, ManagedRecipeKind.Destructible), __instance, (Object?)(object)destructible, isSupplemental: false, disableSourceItemWhenRemoved: false); if (ObjectDropManager.HasVneiRelevantDestructibleOverride(((Object)((Component)destructible).gameObject).name) && ObjectDropManager.TryGetVneiDisplayForDestructible(destructible, out List results)) { RewriteRecipeResults(__instance, results); managedRecipeBinding.LastDisplayFingerprint = BuildResultsFingerprint(results); } } } private static void TreeBaseCtorPostfix(object __instance, [HarmonyArgument(0)] TreeBase treeBase) { if (_available && !((Object)(object)treeBase == (Object)null) && !((Object)(object)((Component)treeBase).gameObject == (Object)null)) { ManagedRecipeBinding managedRecipeBinding = RegisterManagedRecipe(new ManagedRecipeKey(((Object)((Component)treeBase).gameObject).name, ManagedRecipeKind.TreeBase), __instance, (Object?)(object)treeBase, isSupplemental: false, disableSourceItemWhenRemoved: false); if (ObjectDropManager.HasVneiRelevantTreeBaseOverride(((Object)((Component)treeBase).gameObject).name) && ObjectDropManager.TryGetVneiDisplayForTreeBase(treeBase, out List results)) { RewriteRecipeResults(__instance, results); managedRecipeBinding.LastDisplayFingerprint = BuildResultsFingerprint(results); } } } private static void HandleIndexingItemRecipes(GameObject prefab) { try { TryAddMissingContainerRecipe(prefab); TryAddMissingDestructibleRecipe(prefab); TryAddMissingFishRecipe(prefab); } catch (Exception ex) { DropNSpawnPlugin.DropNSpawnLogger.LogWarning((object)("VNEI compatibility failed while indexing '" + ((prefab != null) ? ((Object)prefab).name : null) + "': " + ex.Message)); } } private static void TryAddMissingContainerRecipe(GameObject prefab) { if (ShouldUseSupplementalContainerRecipe(prefab, out List results)) { CreateAndRegisterSupplementalRecipe(new ManagedRecipeKey(((Object)prefab).name, ManagedRecipeKind.Container), (Object)(object)prefab, results, enableSourceItem: false, disableSourceItemWhenRemoved: false); } } private static bool ShouldUseSupplementalContainerRecipe(GameObject? prefab, out List results) { results = new List(); Piece val = default(Piece); Container val2 = default(Container); if ((Object)(object)prefab != (Object)null && prefab.TryGetComponent(ref val) && prefab.TryGetComponent(ref val2) && val2.m_defaultItems != null && val2.m_defaultItems.m_drops.Count == 0 && ObjectDropManager.TryGetVneiDisplayForContainer(prefab, out results)) { return results.Count > 0; } return false; } private static void TryAddMissingDestructibleRecipe(GameObject prefab) { if (ShouldUseSupplementalDestructibleRecipe(prefab, out Destructible destructible, out List results)) { CreateAndRegisterSupplementalRecipe(new ManagedRecipeKey(((Object)prefab).name, ManagedRecipeKind.Destructible), (Object)(object)destructible, results, enableSourceItem: true, disableSourceItemWhenRemoved: true); } } private static bool ShouldUseSupplementalDestructibleRecipe(GameObject? prefab, out Destructible? destructible, out List results) { destructible = null; results = new List(); if ((Object)(object)prefab != (Object)null && prefab.TryGetComponent(ref destructible) && (Object)(object)prefab.GetComponent() == (Object)null && (Object)(object)prefab.GetComponent() == (Object)null && (Object)(object)destructible != (Object)null && ((Object)(object)destructible.m_spawnWhenDestroyed == (Object)null || TryResolveVneiItem(((Object)destructible.m_spawnWhenDestroyed).name) == null) && ObjectDropManager.TryGetVneiDisplayForDestructible(destructible, out results)) { return results.Count > 0; } return false; } private static void TryAddMissingFishRecipe(GameObject prefab) { ManagedRecipeKey key = new ManagedRecipeKey(((Object)prefab).name, ManagedRecipeKind.Fish); if (!ManagedRecipesByKey.ContainsKey(key) && ShouldUseSupplementalFishRecipe(prefab, out Fish fish, out List results)) { CreateAndRegisterSupplementalRecipe(key, (Object)(object)fish, results, enableSourceItem: true, disableSourceItemWhenRemoved: false); } } private static bool ShouldUseSupplementalFishRecipe(GameObject? prefab, out Fish? fish, out List results) { fish = null; results = new List(); if ((Object)(object)prefab != (Object)null && prefab.TryGetComponent(ref fish) && ObjectDropManager.TryGetVneiDisplayForFish(fish, out results)) { return results.Count > 0; } return false; } private static void RefreshCharacterPrefab(string prefabName, HashSet affectedItems) { ManagedRecipeKey key = new ManagedRecipeKey(prefabName, ManagedRecipeKind.CharacterDrop); if (ManagedRecipesByKey.TryGetValue(key, out ManagedRecipeBinding value)) { Object? source = value.Source; CharacterDrop val = (CharacterDrop)(object)((source is CharacterDrop) ? source : null); if (val != null && CharacterDropManager.TryGetVneiDisplayResults(val, out List results)) { RefreshManagedRecipe(key, value, results, affectedItems); } } } private static void RefreshSpawnAreaPrefab(string prefabName, HashSet affectedItems) { ManagedRecipeKey key = new ManagedRecipeKey(prefabName, ManagedRecipeKind.SpawnArea); if (ManagedRecipesByKey.TryGetValue(key, out ManagedRecipeBinding value)) { Object? source = value.Source; SpawnArea val = (SpawnArea)(object)((source is SpawnArea) ? source : null); if (val != null && SpawnerManager.TryGetVneiDisplayForSpawnArea(val, out List results)) { RefreshManagedRecipe(key, value, results, affectedItems); } } } private static void RefreshContainerPrefab(string prefabName, HashSet affectedItems) { ManagedRecipeKey key = new ManagedRecipeKey(prefabName, ManagedRecipeKind.Container); GameObject val = ResolvePrefab(prefabName); List results2; if (ManagedRecipesByKey.TryGetValue(key, out ManagedRecipeBinding value)) { if ((Object)(object)val != (Object)null && ObjectDropManager.TryGetVneiDisplayForContainer(val, out List results)) { RefreshManagedRecipe(key, value, results, affectedItems); } else if (value.IsSupplemental) { RemoveManagedRecipe(key, value, affectedItems); } } else if (ShouldUseSupplementalContainerRecipe(val, out results2)) { CreateAndRegisterSupplementalRecipe(key, (Object)(object)val, results2, enableSourceItem: false, disableSourceItemWhenRemoved: false, affectedItems); } } private static void RefreshDropTablePrefab(string prefabName, ManagedRecipeKind kind, HashSet affectedItems) { ManagedRecipeKey key = new ManagedRecipeKey(prefabName, kind); if (ManagedRecipesByKey.TryGetValue(key, out ManagedRecipeBinding value)) { Object? source = value.Source; GameObject val = (GameObject)(object)((source is GameObject) ? source : null); if (val != null && TryGetDropTableForKind(val, kind, out DropTable dropTable) && dropTable != null && ObjectDropManager.TryGetVneiDisplayForDropTable(val, dropTable, out List results)) { RefreshManagedRecipe(key, value, results, affectedItems); } } } private static void RefreshPickablePrefab(string prefabName, HashSet affectedItems) { ManagedRecipeKey key = new ManagedRecipeKey(prefabName, ManagedRecipeKind.Pickable); if (ManagedRecipesByKey.TryGetValue(key, out ManagedRecipeBinding value)) { Object? source = value.Source; Pickable val = (Pickable)(object)((source is Pickable) ? source : null); if (val != null && ObjectDropManager.TryGetVneiDisplayForPickable(((Component)val).gameObject, val, out List results)) { RefreshManagedRecipe(key, value, results, affectedItems); } } } private static void RefreshFishPrefab(string prefabName, HashSet affectedItems) { ManagedRecipeKey key = new ManagedRecipeKey(prefabName, ManagedRecipeKind.Fish); GameObject val = ResolvePrefab(prefabName); Fish fish; List results2; if (ManagedRecipesByKey.TryGetValue(key, out ManagedRecipeBinding value)) { Object? source = value.Source; Fish val2 = (Fish)(((object)((source is Fish) ? source : null)) ?? ((object)((val != null) ? val.GetComponent() : null))); if ((Object)(object)val2 != (Object)null && ObjectDropManager.TryGetVneiDisplayForFish(val2, out List results)) { RefreshManagedRecipe(key, value, results, affectedItems); } else if (value.IsSupplemental) { RemoveManagedRecipe(key, value, affectedItems); } } else if (ShouldUseSupplementalFishRecipe(val, out fish, out results2)) { CreateAndRegisterSupplementalRecipe(key, (Object)(object)fish, results2, enableSourceItem: true, disableSourceItemWhenRemoved: false, affectedItems); } } private static void RefreshDestructiblePrefab(string prefabName, HashSet affectedItems) { ManagedRecipeKey key = new ManagedRecipeKey(prefabName, ManagedRecipeKind.Destructible); GameObject prefab = ResolvePrefab(prefabName); Destructible destructible; List results2; if (ManagedRecipesByKey.TryGetValue(key, out ManagedRecipeBinding value)) { Object? source = value.Source; Destructible val = (Destructible)(object)((source is Destructible) ? source : null); if (val != null && ObjectDropManager.TryGetVneiDisplayForDestructible(val, out List results)) { RefreshManagedRecipe(key, value, results, affectedItems); } else if (value.IsSupplemental) { RemoveManagedRecipe(key, value, affectedItems); } } else if (ShouldUseSupplementalDestructibleRecipe(prefab, out destructible, out results2)) { CreateAndRegisterSupplementalRecipe(key, (Object)(object)destructible, results2, enableSourceItem: true, disableSourceItemWhenRemoved: true, affectedItems); } } private static void RefreshTreeBasePrefab(string prefabName, HashSet affectedItems) { ManagedRecipeKey key = new ManagedRecipeKey(prefabName, ManagedRecipeKind.TreeBase); if (ManagedRecipesByKey.TryGetValue(key, out ManagedRecipeBinding value)) { Object? source = value.Source; TreeBase val = (TreeBase)(object)((source is TreeBase) ? source : null); if (val != null && ObjectDropManager.TryGetVneiDisplayForTreeBase(val, out List results)) { RefreshManagedRecipe(key, value, results, affectedItems); } } } private static void RefreshManagedRecipe(ManagedRecipeKey key, ManagedRecipeBinding binding, List results, HashSet affectedItems) { if (binding.IsSupplemental && results.Count == 0) { RemoveManagedRecipe(key, binding, affectedItems); return; } string text = BuildResultsFingerprint(results); if (!string.Equals(binding.LastDisplayFingerprint, text, StringComparison.Ordinal)) { affectedItems.UnionWith(CollectRecipeItems(binding.Recipe)); DetachRecipeFromItems(binding.Recipe, removeFromRecipesList: false); RewriteRecipeResults(binding.Recipe, results); ResetAndRecalculateRecipe(binding.Recipe); _addRecipeToItemsMethod?.Invoke(null, new object[1] { binding.Recipe }); affectedItems.UnionWith(CollectRecipeItems(binding.Recipe)); binding.LastDisplayFingerprint = text; if (binding.IsSupplemental && binding.DisableSourceItemWhenRemoved) { SetVneiItemActive(key.PrefabName, active: true); } } } private static void RemoveManagedRecipe(ManagedRecipeKey key, ManagedRecipeBinding binding, HashSet affectedItems) { affectedItems.UnionWith(CollectRecipeItems(binding.Recipe)); DetachRecipeFromItems(binding.Recipe, removeFromRecipesList: true); ManagedRecipesByKey.Remove(key); if (binding.IsSupplemental && binding.DisableSourceItemWhenRemoved) { SetVneiItemActive(key.PrefabName, active: false); } } private static ManagedRecipeBinding RegisterManagedRecipe(ManagedRecipeKey key, object recipe, Object? source, bool isSupplemental, bool disableSourceItemWhenRemoved) { ManagedRecipeBinding managedRecipeBinding = new ManagedRecipeBinding { Recipe = recipe, Source = source, IsSupplemental = isSupplemental, DisableSourceItemWhenRemoved = disableSourceItemWhenRemoved }; ManagedRecipesByKey[key] = managedRecipeBinding; return managedRecipeBinding; } private static void CreateAndRegisterSupplementalRecipe(ManagedRecipeKey key, Object source, List results, bool enableSourceItem, bool disableSourceItemWhenRemoved, HashSet? affectedItems = null) { if (!(_emptyRecipeCtor == null) && !(_addIngredientMethod == null) && !(_addRecipeToItemsMethod == null)) { object obj = _emptyRecipeCtor.Invoke(Array.Empty()); AddIngredient(obj, source.name, 1, 1, 1f, 1, 1, 1f); RewriteRecipeResults(obj, results); ResetAndRecalculateRecipe(obj); _addRecipeToItemsMethod.Invoke(null, new object[1] { obj }); HashSet hashSet = CollectRecipeItems(obj); if (affectedItems != null) { affectedItems.UnionWith(hashSet); } else { UpdateKnownForItems(hashSet); } RegisterManagedRecipe(key, obj, source, isSupplemental: true, disableSourceItemWhenRemoved).LastDisplayFingerprint = BuildResultsFingerprint(results); if (enableSourceItem) { SetVneiItemActive(source.name, active: true); } } } private static bool ShouldRewriteDropTableRecipe(ManagedRecipeKey key) { return key.Kind switch { ManagedRecipeKind.Container => ObjectDropManager.HasVneiRelevantContainerOverride(key.PrefabName), ManagedRecipeKind.DropOnDestroyed => ObjectDropManager.HasVneiRelevantDropOnDestroyedOverride(key.PrefabName), ManagedRecipeKind.MineRock => ObjectDropManager.HasVneiRelevantMineRockOverride(key.PrefabName), ManagedRecipeKind.MineRock5 => ObjectDropManager.HasVneiRelevantMineRock5Override(key.PrefabName), ManagedRecipeKind.Fish => ObjectDropManager.HasVneiRelevantFishOverride(key.PrefabName), _ => false, }; } private static bool TryGetDropTableRecipeKey(GameObject from, DropTable dropTable, out ManagedRecipeKey key) { Container val = default(Container); if (from.TryGetComponent(ref val) && val.m_defaultItems == dropTable) { key = new ManagedRecipeKey(((Object)from).name, ManagedRecipeKind.Container); return true; } DropOnDestroyed val2 = default(DropOnDestroyed); if (from.TryGetComponent(ref val2) && val2.m_dropWhenDestroyed == dropTable) { key = new ManagedRecipeKey(((Object)from).name, ManagedRecipeKind.DropOnDestroyed); return true; } MineRock val3 = default(MineRock); if (from.TryGetComponent(ref val3) && val3.m_dropItems == dropTable) { key = new ManagedRecipeKey(((Object)from).name, ManagedRecipeKind.MineRock); return true; } MineRock5 val4 = default(MineRock5); if (from.TryGetComponent(ref val4) && val4.m_dropItems == dropTable) { key = new ManagedRecipeKey(((Object)from).name, ManagedRecipeKind.MineRock5); return true; } Fish val5 = default(Fish); if (from.TryGetComponent(ref val5) && val5.m_extraDrops == dropTable) { key = new ManagedRecipeKey(((Object)from).name, ManagedRecipeKind.Fish); return true; } key = default(ManagedRecipeKey); return false; } private static bool TryGetDropTableForKind(GameObject prefab, ManagedRecipeKind kind, out DropTable? dropTable) { dropTable = null; switch (kind) { case ManagedRecipeKind.Container: { Container val2 = default(Container); if (prefab.TryGetComponent(ref val2)) { dropTable = val2.m_defaultItems; return true; } return false; } case ManagedRecipeKind.DropOnDestroyed: { DropOnDestroyed val5 = default(DropOnDestroyed); if (prefab.TryGetComponent(ref val5)) { dropTable = val5.m_dropWhenDestroyed; return true; } return false; } case ManagedRecipeKind.MineRock: { MineRock val3 = default(MineRock); if (prefab.TryGetComponent(ref val3)) { dropTable = val3.m_dropItems; return true; } return false; } case ManagedRecipeKind.MineRock5: { MineRock5 val4 = default(MineRock5); if (prefab.TryGetComponent(ref val4)) { dropTable = val4.m_dropItems; return true; } return false; } case ManagedRecipeKind.Fish: { Fish val = default(Fish); if (prefab.TryGetComponent(ref val)) { dropTable = val.m_extraDrops; return true; } return false; } default: return false; } } private static GameObject? ResolvePrefab(string prefabName) { ZNetScene instance = ZNetScene.instance; if (instance == null) { return null; } return instance.GetPrefab(prefabName); } private static void RewriteRecipeResults(object recipe, List results) { if (!(_resultsProperty?.GetValue(recipe) is IDictionary dictionary)) { return; } dictionary.Clear(); foreach (VneiRecipeResult result in results) { AddResult(recipe, result.PrefabName, result.GroupMin, result.GroupMax, result.GroupChance, result.Min, result.Max, result.Chance); } MethodInfo? combineGroupAmountsMethod = _combineGroupAmountsMethod; if ((object)combineGroupAmountsMethod != null) { object[] parameters = new IDictionary[1] { dictionary }; combineGroupAmountsMethod.Invoke(recipe, parameters); } } private static void ResetAndRecalculateRecipe(object recipe) { _recipeIsOnBlacklistBackingField?.SetValue(recipe, false); _recipeCalculateIsOnBlacklistMethod?.Invoke(recipe, Array.Empty()); _recipeCalculateWidthMethod?.Invoke(recipe, Array.Empty()); _recipeUpdateKnownMethod?.Invoke(recipe, Array.Empty()); } private static HashSet CollectRecipeItems(object recipe) { HashSet hashSet = new HashSet(); foreach (object item in EnumerateRecipeItems(recipe)) { hashSet.Add(item); } return hashSet; } private static IEnumerable EnumerateRecipeItems(object recipe) { foreach (object item in EnumerateItemsFromGroupDictionary(_ingredientsProperty?.GetValue(recipe) as IDictionary)) { yield return item; } foreach (object item2 in EnumerateItemsFromGroupDictionary(_resultsProperty?.GetValue(recipe) as IDictionary)) { yield return item2; } if (!(_stationsProperty?.GetValue(recipe) is IEnumerable enumerable)) { yield break; } foreach (object item3 in enumerable) { object obj = ((item3 == null) ? null : _partItemField?.GetValue(item3)); if (obj != null) { yield return obj; } } } private static IEnumerable EnumerateItemsFromGroupDictionary(IDictionary? groups) { if (groups == null) { yield break; } foreach (DictionaryEntry group in groups) { if (!(group.Value is IEnumerable enumerable)) { continue; } foreach (object item in enumerable) { object obj = ((item == null) ? null : _partItemField?.GetValue(item)); if (obj != null) { yield return obj; } } } } private static void DetachRecipeFromItems(object recipe, bool removeFromRecipesList) { if (_itemResultField == null || _itemIngredientField == null) { return; } if (_ingredientsProperty?.GetValue(recipe) is IDictionary dictionary) { foreach (DictionaryEntry item in dictionary) { if (!(item.Value is IEnumerable enumerable)) { continue; } foreach (object item2 in enumerable) { object obj = ((item2 == null) ? null : _partItemField?.GetValue(item2)); if (obj != null) { RemoveRecipeReference(_itemIngredientField.GetValue(obj), recipe); } } } } if (_resultsProperty?.GetValue(recipe) is IDictionary dictionary2) { foreach (DictionaryEntry item3 in dictionary2) { if (!(item3.Value is IEnumerable enumerable2)) { continue; } foreach (object item4 in enumerable2) { object obj2 = ((item4 == null) ? null : _partItemField?.GetValue(item4)); if (obj2 != null) { RemoveRecipeReference(_itemResultField.GetValue(obj2), recipe); } } } } if (_stationsProperty?.GetValue(recipe) is IEnumerable enumerable3) { foreach (object item5 in enumerable3) { object obj3 = ((item5 == null) ? null : _partItemField?.GetValue(item5)); if (obj3 != null) { RemoveRecipeReference(_itemIngredientField.GetValue(obj3), recipe); } } } if (removeFromRecipesList && _recipesProperty?.GetValue(null) is IList list) { list.Remove(recipe); } } private static void UpdateKnownForItems(IEnumerable items) { foreach (object item in items.Distinct()) { _itemUpdateKnownMethod?.Invoke(item, Array.Empty()); } } private static void RemoveRecipeReference(object? collection, object recipe) { collection?.GetType().GetMethod("Remove", new Type[1] { recipe.GetType() })?.Invoke(collection, new object[1] { recipe }); } private static void AddIngredient(object recipe, string prefabName, int groupMin, int groupMax, float groupChance, int min, int max, float chance) { if (!(_addIngredientMethod == null)) { _addIngredientMethod.Invoke(recipe, new object[4] { prefabName, CreateAmount(groupMin, groupMax, groupChance), CreateAmount(min, max, chance), 1 }); } } private static void AddResult(object recipe, string prefabName, int groupMin, int groupMax, float groupChance, int min, int max, float chance) { if (!(_addResultMethod == null)) { _addResultMethod.Invoke(recipe, new object[4] { prefabName, CreateAmount(groupMin, groupMax, groupChance), CreateAmount(min, max, chance), 1 }); } } private static object CreateAmount(int min, int max, float chance) { if (_singleAmountCtor == null || _rangeAmountCtor == null) { throw new InvalidOperationException("VNEI Amount constructors are unavailable."); } if (min == max) { return _singleAmountCtor.Invoke(new object[2] { min, chance }); } return _rangeAmountCtor.Invoke(new object[3] { min, max, chance }); } private static object? TryResolveVneiItem(string prefabName) { return _getItemMethod?.Invoke(null, new object[1] { prefabName }); } private static void SetVneiItemActive(string prefabName, bool active) { object obj = TryResolveVneiItem(prefabName); if (obj != null && _itemIsActiveField != null) { _itemIsActiveField.SetValue(obj, active); } } private static string BuildResultsFingerprint(List results) { if (results == null || results.Count == 0) { return ""; } StringBuilder stringBuilder = new StringBuilder(); foreach (VneiRecipeResult result in results) { stringBuilder.Append(result.PrefabName ?? "").Append('\n').Append(result.GroupMin) .Append('\n') .Append(result.GroupMax) .Append('\n') .Append(result.GroupChance.ToString("R")) .Append('\n') .Append(result.Min) .Append('\n') .Append(result.Max) .Append('\n') .Append(result.Chance.ToString("R")) .Append('\n'); } return stringBuilder.ToString(); } } internal readonly struct VneiRecipeResult { public readonly string PrefabName; public readonly int GroupMin; public readonly int GroupMax; public readonly float GroupChance; public readonly int Min; public readonly int Max; public readonly float Chance; public VneiRecipeResult(string prefabName, int groupMin, int groupMax, float groupChance, int min, int max, float chance) { PrefabName = prefabName; GroupMin = groupMin; GroupMax = groupMax; GroupChance = groupChance; Min = min; Max = max; Chance = chance; } } internal static class VanillaPrefabCatalog { private enum CatalogState { Uninitialized, Loaded, Unavailable } private static readonly object Sync = new object(); private static readonly HashSet PrefabNames = new HashSet(StringComparer.OrdinalIgnoreCase); private static CatalogState _state; internal static bool IsAvailable { get { EnsureLoaded(); return _state == CatalogState.Loaded; } } internal static bool IsVanilla(string prefabName) { EnsureLoaded(); if (_state == CatalogState.Loaded && !string.IsNullOrWhiteSpace(prefabName)) { return PrefabNames.Contains(prefabName); } return false; } private static void EnsureLoaded() { if (_state != CatalogState.Uninitialized) { return; } lock (Sync) { if (_state != CatalogState.Uninitialized) { return; } string text = Path.Combine(Application.dataPath, "StreamingAssets", "SoftRef", "manifest_extended"); if (!File.Exists(text)) { _state = CatalogState.Unavailable; DropNSpawnPlugin.DropNSpawnLogger.LogWarning((object)("Vanilla prefab manifest was not found at '" + text + "'. Prefab owner grouping will still track bundle-backed modded prefabs, but unmapped prefabs will fall under 'Unknown / Untracked'.")); return; } foreach (string item in File.ReadLines(text)) { int num = item.IndexOf("path in bundle:", StringComparison.OrdinalIgnoreCase); if (num < 0) { continue; } string text2 = item.Substring(num + "path in bundle:".Length).Trim(); if (text2.EndsWith(".prefab", StringComparison.OrdinalIgnoreCase)) { string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(text2); if (!string.IsNullOrWhiteSpace(fileNameWithoutExtension)) { PrefabNames.Add(fileNameWithoutExtension); } } } _state = CatalogState.Loaded; } } } } 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; } } }