Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of BalrondNatureOptimizer v0.7.7
BalrondNatureOptimizer.dll
Decompiled a month ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using UnityEngine; using UnityEngine.Rendering; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyTitle("BalrondNatureOptimizer")] [assembly: AssemblyDescription("Compatibility and performance optimizer for Balrond Amazing Nature 1.3.8")] [assembly: AssemblyCompany("DragonMotion")] [assembly: AssemblyProduct("BalrondNatureOptimizer")] [assembly: AssemblyFileVersion("0.7.7.0")] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: AssemblyVersion("0.7.7.0")] namespace DragonMotion.BalrondNatureOptimizer; [BepInPlugin("dragonmotion.balrondnatureoptimizer", "BalrondNatureOptimizer", "0.7.7")] [BepInDependency("balrond.astafaraios.BalrondAmazingNature", "1.3.8")] public sealed class BalrondNatureOptimizerPlugin : BaseUnityPlugin { public const string PluginGuid = "dragonmotion.balrondnatureoptimizer"; public const string PluginName = "BalrondNatureOptimizer"; public const string PluginVersion = "0.7.7"; public const string BalrondGuid = "balrond.astafaraios.BalrondAmazingNature"; public const string SupportedBalrondVersion = "1.3.8"; private void Awake() { OptimizerRuntime.Initialize(((BaseUnityPlugin)this).Config, ((BaseUnityPlugin)this).Logger); OptimizerRuntime.Apply(); } private void OnDestroy() { OptimizerRuntime.Shutdown(); } } internal static class OptimizerRuntime { private struct SavedFloat { internal bool HasValue; internal float Value; } private struct MaterialRenderState { internal bool Active; internal bool RestoreAlphaState; internal int RenderQueue; internal bool AlphaTestUnderscore; internal bool AlphaTestPlain; internal SavedFloat Cutoff; internal SavedFloat AlphaCutoff; internal SavedFloat CutoffThreshold; internal SavedFloat Mode; internal SavedFloat ZWrite; internal SavedFloat SrcBlend; internal SavedFloat DstBlend; internal SavedFloat Cull; } private struct ClutterOriginalState { internal int Amount; internal bool Enabled; } internal sealed class RuntimeOptions { internal bool Enabled; internal bool DisableMonsterClimbing; internal bool ReplaceStableHashReflection; internal bool DisableServingTray; internal bool DisableServingTrayAutoRegistration; internal bool ExcludeHeavyFoodDisplays; internal bool DisablePoisonGeysers; internal bool OptimizeEnabledPoisonGeysers; internal int GeyserParticleLimit; internal int GeyserMaxNear; internal int GeyserMaxTotal; internal bool DisableSwampClutter; internal bool DisableSwampVisualVegetation; internal bool DisableSwampResourceVegetation; internal int BalrondClutterDensityPercent; internal bool DisableNonInstancedBalrondClutter; internal bool OptimizePortalEffects; internal int PortalParticleLimit; internal bool OptimizeFloatingDebrisEffects; internal int FloatingDebrisParticleLimit; internal bool ConservativeMaterialStateFix; internal bool AggressiveOpaqueMaterialRestore; internal bool DisableKnownHeavyPrefabShadows; internal bool FixBalrondConstructionsNullHitEffects; internal bool SmoothDistantTerrainRebuild; internal int DistantTerrainRegionsPerFrame; internal bool CacheVanillaZdoSortComparers; internal float StaticPhysicsRecheckInterval; internal bool TimeSliceTerrainClutterRebuilds; } private sealed class LegacyValues { internal HashSet<ConfigDefinition> ExistingDefinitions; internal bool Enabled; internal bool ReplaceStableHashReflection; internal bool EnableServingTrayIntegration; internal bool DisableServingTrayAutoRegistration; internal bool ExcludeHeavyFoodDisplays; internal bool DisablePoisonGeysers; internal bool OptimizeEnabledPoisonGeysers; internal int GeyserParticleLimit; internal int GeyserMaxNear; internal int GeyserMaxTotal; internal bool DisableSwampClutter; internal bool DisableSwampVisualVegetation; internal bool DisableSwampResourceVegetation; internal int BalrondClutterDensityPercent; internal bool DisableNonInstancedBalrondClutter; internal bool ConservativeMaterialStateFix; internal bool AggressiveOpaqueMaterialRestore; internal bool DisableKnownHeavyPrefabShadows; internal bool FixBalrondConstructionsNullHitEffects; internal bool SmoothDistantTerrainRebuild; internal int DistantTerrainRegionsPerFrame; internal bool CacheVanillaZdoSortComparers; internal float StaticPhysicsRecheckInterval; internal bool TimeSliceTerrainClutterRebuilds; } private sealed class TerrainLodCycle { internal object[] Entries; internal Heightmap[] Heightmaps; internal Vector3[] Offsets; internal HMBuildData[] Data; internal int Count; internal int Next; internal bool Active; internal Vector3 LastPoint; internal WorldGenerator World; internal IList Source; internal TerrainLod Owner; } private static readonly string[] VegetationNameTokens = new string[12] { "grass", "flower", "plant", "leaf", "leaves", "bush", "shrub", "heath", "fern", "moss", "vine", "branch" }; private const string BalrondConstructionsGuid = "balrond.astafaraios.BalrondConstructions"; private static readonly HashSet<string> HeavyServingTrayPrefabs = new HashSet<string>(StringComparer.Ordinal) { "GoatStew_bal", "ShrededMeat_bal", "MeatBalls_bal", "Surstromming_bal" }; private static readonly List<GameObject> EmptyPrefabList = new List<GameObject>(0); private static readonly HashSet<string> SwampClutterPrefabs = new HashSet<string>(StringComparer.Ordinal) { "instanced_mushroom_brown_bal", "instanced_shrub_brown_bal", "instanced_brown_grass_short_bal", "instanced_dead_straw_short_bal", "ormbunke_bal" }; private static readonly HashSet<string> SwampVisualVegetationPrefabs = new HashSet<string>(StringComparer.Ordinal) { "WetTree1_bal", "WetTree2_bal", "WetTree3_bal", "LinedSwampTreeLarge_bal", "LinedSwampTreeLarge2_bal", "Oak_Swamp_bal", "sapling_Swamp_bal" }; private static readonly HashSet<string> SwampResourceVegetationPrefabs = new HashSet<string>(StringComparer.Ordinal) { "MineRock_Guck_bal", "Pickable_GuckSack_bal" }; private static readonly HashSet<string> KnownHeavyShadowPrefabs = new HashSet<string>(StringComparer.Ordinal) { "piece_waterwell_bal", "scrapsmelter_ext1_bal", "raw_wood_roof_top_bal", "raw_wood_roof_45d_top_bal", "StoneboundKiln_bal" }; private static readonly string[] BalrondConstructionsNullHitEffectPrefabs = new string[5] { "core_wood_wall_2_bal", "core_wood_wall_4_bal", "core_wood_wall_corner_bal", "core_wood_wall_deco2_bal", "core_wood_wall_deco4_bal" }; private static readonly Dictionary<Clutter, ClutterOriginalState> OriginalBalrondClutterStates = new Dictionary<Clutter, ClutterOriginalState>(); internal const string BalrondGuid = "balrond.astafaraios.BalrondAmazingNature"; internal const string SupportedBalrondVersion = "1.3.8"; private static readonly Version SupportedBalrondVersionValue = new Version(1, 3, 8); internal static readonly Harmony Patcher = new Harmony("dragonmotion.balrondnatureoptimizer"); internal static ManualLogSource Log; internal static RuntimeOptions Options; internal static ConfigEntry<bool> OptimizerEnabled; internal static ConfigEntry<bool> DisableMonsterClimbing; internal static ConfigEntry<bool> ReplaceStableHashReflection; internal static ConfigEntry<bool> EnableServingTrayIntegration; internal static ConfigEntry<bool> DisableServingTrayAutoRegistration; internal static ConfigEntry<bool> ExcludeHeavyFoodDisplays; internal static ConfigEntry<bool> DisablePoisonGeysers; internal static ConfigEntry<bool> OptimizeEnabledPoisonGeysers; internal static ConfigEntry<int> GeyserParticleLimit; internal static ConfigEntry<int> GeyserMaxNear; internal static ConfigEntry<int> GeyserMaxTotal; internal static ConfigEntry<bool> DisableSwampClutter; internal static ConfigEntry<bool> DisableSwampVisualVegetation; internal static ConfigEntry<bool> DisableSwampResourceVegetation; internal static ConfigEntry<int> BalrondClutterDensityPercent; internal static ConfigEntry<bool> DisableNonInstancedBalrondClutter; internal static ConfigEntry<bool> OptimizePortalEffects; internal static ConfigEntry<int> PortalParticleLimit; internal static ConfigEntry<bool> OptimizeFloatingDebrisEffects; internal static ConfigEntry<int> FloatingDebrisParticleLimit; internal static ConfigEntry<bool> ConservativeMaterialStateFix; internal static ConfigEntry<bool> AggressiveOpaqueMaterialRestore; internal static ConfigEntry<bool> DisableKnownHeavyPrefabShadows; internal static ConfigEntry<bool> FixBalrondConstructionsNullHitEffects; internal static ConfigEntry<bool> SmoothDistantTerrainRebuild; internal static ConfigEntry<int> DistantTerrainRegionsPerFrame; internal static ConfigEntry<bool> CacheVanillaZdoSortComparers; internal static ConfigEntry<float> StaticPhysicsRecheckInterval; internal static ConfigEntry<bool> TimeSliceTerrainClutterRebuilds; private static readonly Dictionary<string, Type> TypeCache = new Dictionary<string, Type>(StringComparer.Ordinal); private static readonly Dictionary<Type, Dictionary<string, FieldInfo>> FieldCache = new Dictionary<Type, Dictionary<string, FieldInfo>>(); private static readonly List<string> StartupIssues = new List<string>(); private static Assembly _balrondAssembly; private static ConfigFile _config; private static bool _initialized; private static bool _settingsCaptured; private static bool _restartWarningLogged; private static bool _collectStartupIssues; private static Comparison<ZDO> _zNetSceneZdoComparison; private static Comparison<ZDO> _serverSendZdoComparison; private static Comparison<ZDO> _clientSendZdoComparison; private static float _staticPhysicsRecheckInterval; private static readonly Dictionary<int, TerrainLodCycle> TerrainLodCycles = new Dictionary<int, TerrainLodCycle>(); private static FieldInfo _terrainLodHeightmapsField; private static FieldInfo _terrainLodTopStateField; private static FieldInfo _terrainEntryHeightmapField; private static FieldInfo _terrainEntryOffsetField; private static FieldInfo _terrainEntryStateField; private static int _terrainReadyState; private static int _terrainDoneState; private static Action<object, int> _terrainEntryStateSetter; private static Action<TerrainLod, int> _terrainTopStateSetter; private static FieldRef<Heightmap, HMBuildData> _heightmapBuildDataRef; private static FieldRef<Heightmap, Biome[]> _heightmapCornerBiomesRef; private static bool _terrainLodCompatibilityChecked; private static bool _terrainLodSmoothingFaulted; private static int _terrainBudgetFrame = -1; private static int _terrainRegionsProcessedThisFrame; private static void ReplaceShaderPrefix(Material material, string oldShaderName, ref MaterialRenderState __state) { __state = default(MaterialRenderState); if (!Options.ConservativeMaterialStateFix || (Object)(object)material == (Object)null || !ShouldGuardShaderReplacement(((Object)material).name, oldShaderName)) { return; } MaterialRenderState materialRenderState = new MaterialRenderState { Active = true, RestoreAlphaState = !HasAuthoredAlphaIntent(material), RenderQueue = material.renderQueue, AlphaTestUnderscore = material.IsKeywordEnabled("_ALPHATEST_ON"), AlphaTestPlain = material.IsKeywordEnabled("ALPHATEST_ON") }; if (materialRenderState.RestoreAlphaState) { CaptureFloat(material, "_Cutoff", ref materialRenderState.Cutoff); CaptureFloat(material, "_AlphaCutoff", ref materialRenderState.AlphaCutoff); CaptureFloat(material, "_CutoffThreshold", ref materialRenderState.CutoffThreshold); if (Options.AggressiveOpaqueMaterialRestore) { CaptureFloat(material, "_Mode", ref materialRenderState.Mode); CaptureFloat(material, "_ZWrite", ref materialRenderState.ZWrite); CaptureFloat(material, "_SrcBlend", ref materialRenderState.SrcBlend); CaptureFloat(material, "_DstBlend", ref materialRenderState.DstBlend); } } CaptureFloat(material, "_Cull", ref materialRenderState.Cull); __state = materialRenderState; } private static bool ShouldGuardShaderReplacement(string materialName, string oldShaderName) { if (string.Equals(oldShaderName, "Balrond/Piece", StringComparison.Ordinal) || string.Equals(oldShaderName, "Balrond/Creature", StringComparison.Ordinal)) { return !LooksLikeVegetation(materialName, oldShaderName); } return false; } private static void CaptureFloat(Material material, string property, ref SavedFloat saved) { if (material.HasProperty(property)) { saved.HasValue = true; saved.Value = material.GetFloat(property); } } private static void ReplaceShaderPostfix(Material material, bool __result, MaterialRenderState __state) { if (!__result || !__state.Active || (Object)(object)material == (Object)null) { return; } if (__state.RestoreAlphaState) { RestoreFloat(material, "_Cutoff", __state.Cutoff, 0f); RestoreFloat(material, "_AlphaCutoff", __state.AlphaCutoff, 0f); RestoreFloat(material, "_CutoffThreshold", __state.CutoffThreshold, 0f); if (Options.AggressiveOpaqueMaterialRestore) { RestoreFloat(material, "_Mode", __state.Mode, 0f); RestoreFloat(material, "_ZWrite", __state.ZWrite, 1f); RestoreFloat(material, "_SrcBlend", __state.SrcBlend, 1f); RestoreFloat(material, "_DstBlend", __state.DstBlend, 0f); if (!__state.AlphaTestUnderscore) { material.DisableKeyword("_ALPHATEST_ON"); } if (!__state.AlphaTestPlain) { material.DisableKeyword("ALPHATEST_ON"); } material.renderQueue = __state.RenderQueue; } } RestoreFloat(material, "_Cull", __state.Cull, 2f); } private static void RestoreFloat(Material material, string property, SavedFloat saved, float fallback) { if (material.HasProperty(property)) { material.SetFloat(property, saved.HasValue ? saved.Value : fallback); } } private static bool ForceAlphaCutoutFixPrefix(Material material) { if (!Options.ConservativeMaterialStateFix || (Object)(object)material == (Object)null || (Object)(object)material.shader == (Object)null) { return true; } string name = ((Object)material.shader).name; if (string.Equals(name, "Custom/StaticRock", StringComparison.Ordinal)) { return false; } if (!string.Equals(name, "Custom/Piece", StringComparison.Ordinal) && !string.Equals(name, "Custom/Creature", StringComparison.Ordinal)) { return true; } return LooksLikeVegetation(((Object)material).name, name); } private static bool HasAuthoredAlphaIntent(Material material) { if (material.IsKeywordEnabled("_ALPHATEST_ON") || material.IsKeywordEnabled("ALPHATEST_ON") || material.IsKeywordEnabled("_ALPHABLEND_ON") || material.IsKeywordEnabled("ALPHABLEND_ON") || material.IsKeywordEnabled("_ALPHAPREMULTIPLY_ON") || material.IsKeywordEnabled("ALPHAPREMULTIPLY_ON") || material.IsKeywordEnabled("_SURFACE_TYPE_TRANSPARENT")) { return true; } if (material.renderQueue >= 2450) { return true; } if (material.HasProperty("_Mode")) { return material.GetFloat("_Mode") >= 0.5f; } return false; } private static bool LooksLikeVegetation(string materialName, string shaderName) { for (int i = 0; i < VegetationNameTokens.Length; i++) { string value = VegetationNameTokens[i]; if ((!string.IsNullOrEmpty(materialName) && materialName.IndexOf(value, StringComparison.OrdinalIgnoreCase) >= 0) || (!string.IsNullOrEmpty(shaderName) && shaderName.IndexOf(value, StringComparison.OrdinalIgnoreCase) >= 0)) { return true; } } return false; } private static bool BalrondStableHashPrefix(string value, ref int __result) { if (value == null) { __result = 0; return false; } __result = ComputeStableHash(value); return false; } private static int ComputeStableHash(string value) { int num = 5381; int num2 = num; for (int i = 0; i < value.Length && value[i] != 0; i += 2) { num = ((num << 5) + num) ^ value[i]; if (i == value.Length - 1 || value[i + 1] == '\0') { break; } num2 = ((num2 << 5) + num2) ^ value[i + 1]; } return num + num2 * 1566083941; } private static bool ServingTraySetupPrefix() { return !Options.DisableServingTray; } private static bool ServingTrayBuildAutoScanListPrefix(ref List<GameObject> __result) { if (!Options.DisableServingTrayAutoRegistration) { return true; } __result = EmptyPrefabList; return false; } private static bool ServingTrayPreparePrefix(GameObject prefab, bool requireConsumable, ref string failureReason, ref bool __result) { bool num = Options.DisableServingTrayAutoRegistration && requireConsumable; bool flag = (Object)(object)prefab != (Object)null && Options.ExcludeHeavyFoodDisplays && HeavyServingTrayPrefabs.Contains(((Object)prefab).name); if (!num && !flag) { return true; } failureReason = "blocked by BalrondNatureOptimizer before prefab mutation"; __result = false; return false; } private static bool MonsterSpawnerSetupPrefix(string name) { if (Options.DisablePoisonGeysers) { return !IsPoisonGeyserName(name); } return true; } private static void ZNetSceneAwakePostfix(ZNetScene __instance) { if ((Object)(object)__instance == (Object)null) { return; } int num = 0; int num2 = 0; int num3 = 0; int num4 = 0; int processedParticleSystems = 0; int num5 = 0; int num6 = 0; int num7 = 0; List<string> list = new List<string>(); if (Options.DisablePoisonGeysers || Options.OptimizeEnabledPoisonGeysers) { GameObject prefab = __instance.GetPrefab("PoisonGeyser_bal"); GameObject prefab2 = __instance.GetPrefab("PoisonGeyserSpawner_bal"); if (Options.DisablePoisonGeysers) { if ((Object)(object)prefab != (Object)null) { NeutralizePoisonGeyser(prefab); num++; } if ((Object)(object)prefab2 != (Object)null) { NeutralizePoisonGeyser(prefab2); num++; } } else { if ((Object)(object)prefab != (Object)null) { OptimizePoisonGeyser(prefab); num++; } if ((Object)(object)prefab2 != (Object)null) { OptimizePoisonGeyser(prefab2); num++; } } } bool num8 = !Application.isBatchMode; if (num8 && Options.OptimizePortalEffects) { if (TryOptimizePortal(__instance.GetPrefab("portal_NatureSmall_bal"), 8, "_target_found_red/Particle System/Point light", expectsLightLod: true, Options.PortalParticleLimit, out var processedParticleSystems2)) { num2++; num3 += processedParticleSystems2; } else { list.Add("portal_NatureSmall_bal"); } if (TryOptimizePortal(__instance.GetPrefab("portal_StoneSmall_bal"), 7, "_target_found_red/Point light", expectsLightLod: false, Options.PortalParticleLimit, out var processedParticleSystems3)) { num2++; num3 += processedParticleSystems3; } else { list.Add("portal_StoneSmall_bal"); } } if (num8 && Options.OptimizeFloatingDebrisEffects) { if (TryOptimizeFloatingDebris(__instance.GetPrefab("FloatingDebris_bal"), Options.FloatingDebrisParticleLimit, out processedParticleSystems)) { num4 = 1; } else { list.Add("FloatingDebris_bal"); } } if (num8 && Options.DisableKnownHeavyPrefabShadows) { foreach (string knownHeavyShadowPrefab in KnownHeavyShadowPrefabs) { GameObject prefab3 = __instance.GetPrefab(knownHeavyShadowPrefab); if (!((Object)(object)prefab3 == (Object)null)) { DisableAllShadows(prefab3); num5++; } } GameObject prefab4 = __instance.GetPrefab("FloatingDebris_bal"); if ((Object)(object)prefab4 != (Object)null) { DisableCastShadowsOnly(prefab4); num5++; } } if (Options.FixBalrondConstructionsNullHitEffects && Chainloader.PluginInfos.ContainsKey("balrond.astafaraios.BalrondConstructions")) { for (int i = 0; i < BalrondConstructionsNullHitEffectPrefabs.Length; i++) { GameObject prefab5 = __instance.GetPrefab(BalrondConstructionsNullHitEffectPrefabs[i]); int num9 = RemoveEnabledNullEffects((((Object)(object)prefab5 == (Object)null) ? null : prefab5.GetComponent<WearNTear>())?.m_hitEffect); if (num9 > 0) { num6++; num7 += num9; } } } Log.LogInfo((object)($"Prefab pass: geysers={num}, portals={num2}/2 ({num3} particle systems), " + $"floatingDebris={num4}/1 ({processedParticleSystems} particle system), shadowTemplates={num5}, " + $"constructionRepairs={num6}/{num7}.")); if (list.Count > 0) { Warn("Exact 1.3.8 prefab signature mismatch; skipped without partial mutation: " + string.Join(", ", list) + "."); } } private static bool TryOptimizePortal(GameObject prefab, int expectedParticleSystems, string lightPath, bool expectsLightLod, int particleLimit, out int processedParticleSystems) { //IL_00e7: Unknown result type (might be due to invalid IL or missing references) //IL_00ec: Unknown result type (might be due to invalid IL or missing references) processedParticleSystems = 0; if ((Object)(object)prefab == (Object)null || (Object)(object)prefab.GetComponent<ZNetView>() == (Object)null || (Object)(object)prefab.GetComponent<TeleportWorld>() == (Object)null) { return false; } ParticleSystem[] componentsInChildren = prefab.GetComponentsInChildren<ParticleSystem>(true); Transform val = prefab.transform.Find(lightPath); Light val2 = (((Object)(object)val == (Object)null) ? null : ((Component)val).GetComponent<Light>()); LightLod val3 = (((Object)(object)val == (Object)null) ? null : ((Component)val).GetComponent<LightLod>()); LightFlicker val4 = (((Object)(object)val == (Object)null) ? null : ((Component)val).GetComponent<LightFlicker>()); bool flag = ((!expectsLightLod) ? ((Object)(object)val3 == (Object)null && (Object)(object)val4 != (Object)null) : ((Object)(object)val3 != (Object)null && (Object)(object)val4 == (Object)null)); if (componentsInChildren.Length != expectedParticleSystems || (Object)(object)val2 == (Object)null || !flag) { return false; } for (int i = 0; i < componentsInChildren.Length; i++) { if ((Object)(object)componentsInChildren[i] == (Object)null) { return false; } } for (int j = 0; j < componentsInChildren.Length; j++) { MainModule main = componentsInChildren[j].main; ((MainModule)(ref main)).cullingMode = (ParticleSystemCullingMode)0; if (((MainModule)(ref main)).maxParticles > particleLimit) { ((MainModule)(ref main)).maxParticles = particleLimit; } } val2.shadows = (LightShadows)0; if ((Object)(object)val3 != (Object)null) { val3.m_shadowLod = false; } processedParticleSystems = componentsInChildren.Length; return true; } private static bool TryOptimizeFloatingDebris(GameObject prefab, int particleLimit, out int processedParticleSystems) { //IL_009e: 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) processedParticleSystems = 0; if ((Object)(object)prefab == (Object)null || (Object)(object)prefab.GetComponent<ZNetView>() == (Object)null || (Object)(object)prefab.GetComponent<Floating>() == (Object)null || (Object)(object)prefab.GetComponent<ZSyncTransform>() == (Object)null || (Object)(object)prefab.GetComponent<Rigidbody>() == (Object)null || (Object)(object)prefab.GetComponentInChildren<Container>(true) == (Object)null) { return false; } ParticleSystem[] componentsInChildren = prefab.GetComponentsInChildren<ParticleSystem>(true); Transform val = prefab.transform.Find("WaterSurface/vfx_water_surface"); ParticleSystem val2 = (((Object)(object)val == (Object)null) ? null : ((Component)val).GetComponent<ParticleSystem>()); if (componentsInChildren.Length != 1 || (Object)(object)val2 == (Object)null || (Object)(object)componentsInChildren[0] != (Object)(object)val2) { return false; } MainModule main = val2.main; ((MainModule)(ref main)).cullingMode = (ParticleSystemCullingMode)0; if (((MainModule)(ref main)).maxParticles > particleLimit) { ((MainModule)(ref main)).maxParticles = particleLimit; } processedParticleSystems = 1; return true; } private static int RemoveEnabledNullEffects(EffectList effectList) { EffectData[] array = effectList?.m_effectPrefabs; if (array == null || array.Length == 0) { return 0; } int num = 0; foreach (EffectData val in array) { if (val != null && val.m_enabled && (Object)(object)val.m_prefab == (Object)null) { num++; } } if (num == 0) { return 0; } EffectData[] array2 = (EffectData[])(object)new EffectData[array.Length - num]; int num2 = 0; foreach (EffectData val2 in array) { if (val2 == null || !val2.m_enabled || !((Object)(object)val2.m_prefab == (Object)null)) { array2[num2++] = val2; } } effectList.m_effectPrefabs = array2; return num; } private static void NeutralizePoisonGeyser(GameObject prefab) { for (int num = prefab.transform.childCount - 1; num >= 0; num--) { Transform child = prefab.transform.GetChild(num); if ((Object)(object)child != (Object)null) { Object.DestroyImmediate((Object)(object)((Component)child).gameObject); } } Component[] components = prefab.GetComponents<Component>(); for (int num2 = components.Length - 1; num2 >= 0; num2--) { Component val = components[num2]; if (!((Object)(object)val == (Object)null) && !(val is Transform) && !(val is ZNetView)) { Object.DestroyImmediate((Object)(object)val); } } } private static void OptimizePoisonGeyser(GameObject prefab) { //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) ParticleSystem[] componentsInChildren = prefab.GetComponentsInChildren<ParticleSystem>(true); for (int i = 0; i < componentsInChildren.Length; i++) { MainModule main = componentsInChildren[i].main; ((MainModule)(ref main)).cullingMode = (ParticleSystemCullingMode)0; if (((MainModule)(ref main)).maxParticles > Options.GeyserParticleLimit) { ((MainModule)(ref main)).maxParticles = Options.GeyserParticleLimit; } } Renderer[] componentsInChildren2 = prefab.GetComponentsInChildren<Renderer>(true); for (int j = 0; j < componentsInChildren2.Length; j++) { if (componentsInChildren2[j] is ParticleSystemRenderer) { componentsInChildren2[j].shadowCastingMode = (ShadowCastingMode)0; componentsInChildren2[j].receiveShadows = false; } } Light[] componentsInChildren3 = prefab.GetComponentsInChildren<Light>(true); for (int k = 0; k < componentsInChildren3.Length; k++) { componentsInChildren3[k].shadows = (LightShadows)0; } SpawnArea[] componentsInChildren4 = prefab.GetComponentsInChildren<SpawnArea>(true); for (int l = 0; l < componentsInChildren4.Length; l++) { if (componentsInChildren4[l].m_maxNear > Options.GeyserMaxNear) { componentsInChildren4[l].m_maxNear = Options.GeyserMaxNear; } if (componentsInChildren4[l].m_maxTotal > Options.GeyserMaxTotal) { componentsInChildren4[l].m_maxTotal = Options.GeyserMaxTotal; } } } private static void DisableAllShadows(GameObject prefab) { Renderer[] componentsInChildren = prefab.GetComponentsInChildren<Renderer>(true); for (int i = 0; i < componentsInChildren.Length; i++) { componentsInChildren[i].shadowCastingMode = (ShadowCastingMode)0; componentsInChildren[i].receiveShadows = false; } } private static void DisableCastShadowsOnly(GameObject prefab) { Renderer[] componentsInChildren = prefab.GetComponentsInChildren<Renderer>(true); for (int i = 0; i < componentsInChildren.Length; i++) { componentsInChildren[i].shadowCastingMode = (ShadowCastingMode)0; } } private static void ZoneSystemSetupLocationsPrefix() { int num = ((GetStaticField("BalrondNature.VegetationBuilder", "ZoneVegetations") is List<ZoneVegetation> list) ? FilterVegetationList(list) : 0); if (num > 0) { Log.LogInfo((object)$"Removed the Swamp biome bit from {num} pending Amazing Nature vegetation definition(s)."); } } private static void ZoneSystemSetupLocationsPostfix(ZoneSystem __instance) { if (!((Object)(object)__instance == (Object)null)) { int num = FilterVegetationList(__instance.m_vegetation); if (num > 0) { Log.LogInfo((object)$"Removed the Swamp biome bit from {num} active Amazing Nature vegetation definition(s)."); } } } private static int FilterVegetationList(List<ZoneVegetation> list) { //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Unknown result type (might be due to invalid IL or missing references) //IL_009f: 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_00aa: Unknown result type (might be due to invalid IL or missing references) if (list == null) { return 0; } int num = 0; for (int num2 = list.Count - 1; num2 >= 0; num2--) { ZoneVegetation val = list[num2]; if (val != null) { string vegetationName = GetVegetationName(val); if ((Options.DisablePoisonGeysers && IsPoisonGeyserName(vegetationName)) | (Options.DisableSwampVisualVegetation && SwampVisualVegetationPrefabs.Contains(vegetationName)) | (Options.DisableSwampResourceVegetation && SwampResourceVegetationPrefabs.Contains(vegetationName))) { if ((int)val.m_biome == 0) { list.RemoveAt(num2); num++; } else if ((val.m_biome & 2) != 0) { val.m_biome = (Biome)(val.m_biome & -3); num++; if ((int)val.m_biome == 0) { list.RemoveAt(num2); } } } } } return num; } private static void ClutterSystemAwakePrefix() { if (GetInstanceField(GetStaticField("BalrondNature.Launch", "clutterBuilder"), "clutterObjects") is List<Clutter> list) { ConfigureBalrondClutter(list, out var scaled, out var disabled); int num = (Options.DisableSwampClutter ? FilterClutterList(list) : 0); if (scaled > 0 || disabled > 0 || num > 0) { Log.LogInfo((object)($"Configured pending Amazing Nature clutter: density={Options.BalrondClutterDensityPercent}%, " + $"scaled={scaled}, nonInstancedDisabled={disabled}, swampDefinitionsChanged={num}.")); } } } private static void ConfigureBalrondClutter(List<Clutter> list, out int scaled, out int disabled) { scaled = 0; disabled = 0; if (list == null) { return; } int num = Mathf.Clamp(Options.BalrondClutterDensityPercent, 0, 100); for (int i = 0; i < list.Count; i++) { Clutter val = list[i]; if (val != null) { if (!OriginalBalrondClutterStates.TryGetValue(val, out var value)) { value = new ClutterOriginalState { Amount = val.m_amount, Enabled = val.m_enabled }; OriginalBalrondClutterStates.Add(val, value); } int num2 = (val.m_amount = (value.Amount * num + 50) / 100); val.m_enabled = value.Enabled && (!Options.DisableNonInstancedBalrondClutter || val.m_instanced); if (num2 != value.Amount) { scaled++; } if (value.Enabled && !val.m_enabled) { disabled++; } } } } private static int FilterClutterList(List<Clutter> list) { //IL_0030: 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) //IL_0050: 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_0062: Unknown result type (might be due to invalid IL or missing references) if (list == null) { return 0; } int num = 0; for (int num2 = list.Count - 1; num2 >= 0; num2--) { Clutter val = list[num2]; if (val != null && SwampClutterPrefabs.Contains(GetClutterName(val))) { if ((int)val.m_biome == 0) { list.RemoveAt(num2); num++; } else if ((val.m_biome & 2) != 0) { val.m_biome = (Biome)(val.m_biome & -3); num++; if ((int)val.m_biome == 0) { list.RemoveAt(num2); } } } } return num; } private static string GetVegetationName(ZoneVegetation vegetation) { if (string.IsNullOrEmpty(vegetation.m_name)) { if (!((Object)(object)vegetation.m_prefab == (Object)null)) { return ((Object)vegetation.m_prefab).name; } return string.Empty; } return vegetation.m_name; } private static string GetClutterName(Clutter clutter) { if ((Object)(object)clutter.m_prefab != (Object)null && !string.IsNullOrEmpty(((Object)clutter.m_prefab).name)) { return ((Object)clutter.m_prefab).name; } string text; if (string.IsNullOrEmpty(clutter.m_name) || !clutter.m_name.StartsWith("balrond_", StringComparison.Ordinal)) { text = clutter.m_name; if (text == null) { return string.Empty; } } else { text = clutter.m_name.Substring("balrond_".Length); } return text; } private static bool IsPoisonGeyserName(string name) { if (!string.Equals(name, "PoisonGeyser_bal", StringComparison.Ordinal)) { return string.Equals(name, "PoisonGeyserSpawner_bal", StringComparison.Ordinal); } return true; } private static void GameShutdownPostfix() { ClearCollection(GetStaticField("BalrondNature.WorldGenPatches", "takenNamesCache")); ClearCollection(GetStaticField("BalrondNature.WorldGenPatches+ClutterSystem_Awake_Patch", "PatchedClutterSystems")); ResetOwnRuntimeState(); ResetStreamingSessionState(); } private static void ResetOwnRuntimeState() { RestoreBalrondClutterStates(); } private static void ResetPluginBindings() { ResetOwnRuntimeState(); ResetStreamingBindings(); } private static void RestoreBalrondClutterStates() { foreach (KeyValuePair<Clutter, ClutterOriginalState> originalBalrondClutterState in OriginalBalrondClutterStates) { if (originalBalrondClutterState.Key != null) { originalBalrondClutterState.Key.m_amount = originalBalrondClutterState.Value.Amount; originalBalrondClutterState.Key.m_enabled = originalBalrondClutterState.Value.Enabled; } } OriginalBalrondClutterStates.Clear(); } internal static void Initialize(ConfigFile config, ManualLogSource logger) { //IL_0217: Unknown result type (might be due to invalid IL or missing references) //IL_0221: Expected O, but got Unknown //IL_0264: Unknown result type (might be due to invalid IL or missing references) //IL_026e: Expected O, but got Unknown //IL_02b2: Unknown result type (might be due to invalid IL or missing references) //IL_02bc: Expected O, but got Unknown //IL_03b1: Unknown result type (might be due to invalid IL or missing references) //IL_03bb: Expected O, but got Unknown //IL_0464: Unknown result type (might be due to invalid IL or missing references) //IL_046e: Expected O, but got Unknown //IL_04be: Unknown result type (might be due to invalid IL or missing references) //IL_04c8: Expected O, but got Unknown //IL_0617: Unknown result type (might be due to invalid IL or missing references) //IL_0621: Expected O, but got Unknown //IL_06aa: Unknown result type (might be due to invalid IL or missing references) //IL_06b4: Expected O, but got Unknown Log = logger; if (_initialized) { return; } _initialized = true; _config = config; bool saveOnConfigSet = config.SaveOnConfigSet; config.SaveOnConfigSet = false; try { LegacyValues legacyValues = ReadAndRemoveLegacyConfig(config); OptimizerEnabled = config.Bind<bool>("01 - General", "Enable Optimizer", true, RestartRequired("Master switch. When false, this plugin installs no patches and changes no prefab templates.")); ApplyLegacyValue(OptimizerEnabled, legacyValues.Enabled, legacyValues, "General", "Enable Optimizer"); ReplaceStableHashReflection = config.Bind<bool>("02 - Amazing Nature Runtime", "Replace StableHash Reflection", true, RestartRequired("Replace BalrondHashCompat MethodInfo.Invoke calls with Valheim's exact allocation-free stable-hash algorithm.")); ApplyLegacyValue(ReplaceStableHashReflection, legacyValues.ReplaceStableHashReflection, legacyValues, "Balrond Status Effects", "Replace StableHash Reflection"); DisableMonsterClimbing = config.Bind<bool>("02 - Amazing Nature Runtime", "Disable Monster Climbing", false, RestartRequired("Remove Amazing Nature's global monster climbing postfix. Off by default because disabling it changes monster movement around steep terrain and base walls.")); EnableServingTrayIntegration = config.Bind<bool>("03 - Serving Tray", "Enable Serving Tray Integration", false, RestartRequired("Allow Amazing Nature to mutate food prefabs into build pieces. Off by default because every spawned copy then gains Piece/WearNTear behavior.")); ApplyLegacyValue(EnableServingTrayIntegration, legacyValues.EnableServingTrayIntegration, legacyValues, "Balrond Serving Tray", "Enable Serving Tray Integration"); DisableServingTrayAutoRegistration = config.Bind<bool>("03 - Serving Tray", "Disable Automatic Consumable Registration", true, RestartRequired("When Serving Tray is enabled, keep only Amazing Nature's explicit manual entries instead of automatically registering every consumable.")); ApplyLegacyValue(DisableServingTrayAutoRegistration, legacyValues.DisableServingTrayAutoRegistration, legacyValues, "Balrond Serving Tray", "Disable Automatic Consumable Registration"); ExcludeHeavyFoodDisplays = config.Bind<bool>("03 - Serving Tray", "Exclude Extremely Heavy Food Displays", true, RestartRequired("Block four audited 196k-243k triangle food displays from new Serving Tray registration.")); ApplyLegacyValue(ExcludeHeavyFoodDisplays, legacyValues.ExcludeHeavyFoodDisplays, legacyValues, "Balrond Serving Tray", "Exclude Extremely Heavy Food Displays"); DisablePoisonGeysers = config.Bind<bool>("04 - Swamp", "Disable Poison Geysers", true, RestartRequired("Remove poison geysers from Swamp generation and reduce their registered templates to lightweight network roots.")); ApplyLegacyValue(DisablePoisonGeysers, legacyValues.DisablePoisonGeysers, legacyValues, "Balrond Swamp", "Disable Poison Geysers"); OptimizeEnabledPoisonGeysers = config.Bind<bool>("04 - Swamp", "Optimize Poison Geysers When Enabled", true, RestartRequired("When geysers remain enabled, apply particle, light-shadow, and SpawnArea caps once to their exact templates.")); ApplyLegacyValue(OptimizeEnabledPoisonGeysers, legacyValues.OptimizeEnabledPoisonGeysers, legacyValues, "Balrond Swamp", "Optimize Poison Geysers When Enabled"); GeyserParticleLimit = config.Bind<int>("04 - Swamp", "Geyser Max Particles Per System", 512, new ConfigDescription(RestartRequired("Maximum particles for each enabled poison-geyser particle system."), (AcceptableValueBase)(object)new AcceptableValueRange<int>(32, 2000), Array.Empty<object>())); ApplyLegacyValue(GeyserParticleLimit, legacyValues.GeyserParticleLimit, legacyValues, "Balrond Swamp", "Geyser Max Particles Per System"); GeyserMaxNear = config.Bind<int>("04 - Swamp", "Geyser Spawner Max Near", 4, new ConfigDescription(RestartRequired("Maximum nearby monsters for PoisonGeyserSpawner_bal."), (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 20), Array.Empty<object>())); ApplyLegacyValue(GeyserMaxNear, legacyValues.GeyserMaxNear, legacyValues, "Balrond Swamp", "Geyser Spawner Max Near"); GeyserMaxTotal = config.Bind<int>("04 - Swamp", "Geyser Spawner Max Total", 12, new ConfigDescription(RestartRequired("Maximum total monsters for PoisonGeyserSpawner_bal."), (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 100), Array.Empty<object>())); ApplyLegacyValue(GeyserMaxTotal, legacyValues.GeyserMaxTotal, legacyValues, "Balrond Swamp", "Geyser Spawner Max Total"); DisableSwampClutter = config.Bind<bool>("04 - Swamp", "Disable Balrond Swamp Clutter", true, RestartRequired("Remove only the Swamp biome bit from five Amazing Nature clutter definitions.")); ApplyLegacyValue(DisableSwampClutter, legacyValues.DisableSwampClutter, legacyValues, "Balrond Swamp", "Disable Balrond Swamp Clutter"); DisableSwampVisualVegetation = config.Bind<bool>("04 - Swamp", "Disable Balrond Swamp World Vegetation", true, RestartRequired("Remove only the Swamp biome bit from Amazing Nature's extra swamp trees and saplings.")); ApplyLegacyValue(DisableSwampVisualVegetation, legacyValues.DisableSwampVisualVegetation, legacyValues, "Balrond Swamp", "Disable Balrond Swamp World Vegetation"); DisableSwampResourceVegetation = config.Bind<bool>("04 - Swamp", "Disable Balrond Swamp Resource Vegetation", false, RestartRequired("Also remove Amazing Nature MineRock_Guck and Pickable_GuckSack generation from Swamp. Off by default to preserve resources.")); ApplyLegacyValue(DisableSwampResourceVegetation, legacyValues.DisableSwampResourceVegetation, legacyValues, "Balrond Swamp", "Disable Balrond Swamp Resource Vegetation"); BalrondClutterDensityPercent = config.Bind<int>("05 - Balrond Clutter", "Balrond Clutter Density Percent", 50, new ConfigDescription(RestartRequired("Scale only Amazing Nature clutter placement attempts."), (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 100), Array.Empty<object>())); ApplyLegacyValue(BalrondClutterDensityPercent, legacyValues.BalrondClutterDensityPercent, legacyValues, "Balrond Streaming", "Balrond Clutter Density Percent"); DisableNonInstancedBalrondClutter = config.Bind<bool>("05 - Balrond Clutter", "Disable Non-Instanced Balrond Clutter", true, RestartRequired("Disable Amazing Nature clutter entries that instantiate separate GameObjects instead of using InstanceRenderer.")); ApplyLegacyValue(DisableNonInstancedBalrondClutter, legacyValues.DisableNonInstancedBalrondClutter, legacyValues, "Balrond Streaming", "Disable Non-Instanced Balrond Clutter"); OptimizePortalEffects = config.Bind<bool>("06 - Rendering", "Optimize Portal Effects", true, RestartRequired("On the two exact 1.3.8 small-portal templates, use Automatic particle culling, cap persistent particles, and disable only the portal light's shadows.")); PortalParticleLimit = config.Bind<int>("06 - Rendering", "Portal Max Particles Per System", 384, new ConfigDescription(RestartRequired("Maximum particles per system on portal_NatureSmall_bal and portal_StoneSmall_bal."), (AcceptableValueBase)(object)new AcceptableValueRange<int>(128, 1000), Array.Empty<object>())); OptimizeFloatingDebrisEffects = config.Bind<bool>("06 - Rendering", "Optimize Floating Debris Effects", true, RestartRequired("On FloatingDebris_bal, use Automatic culling and cap only its exact persistent water-surface particle system.")); FloatingDebrisParticleLimit = config.Bind<int>("06 - Rendering", "Floating Debris Max Particles", 256, new ConfigDescription(RestartRequired("Maximum particles for FloatingDebris_bal/WaterSurface/vfx_water_surface."), (AcceptableValueBase)(object)new AcceptableValueRange<int>(64, 1000), Array.Empty<object>())); ConservativeMaterialStateFix = config.Bind<bool>("06 - Rendering", "Conservative Material State Fix", false, RestartRequired("Guard authored cutoff and culling state while Amazing Nature 1.3.8 replaces non-vegetation Piece, Creature, and StaticRock shaders. Off by default because it changes first-render material state.")); ApplyLegacyValue(ConservativeMaterialStateFix, legacyValues.ConservativeMaterialStateFix, legacyValues, "Balrond Shaders", "Conservative Material State Fix"); AggressiveOpaqueMaterialRestore = config.Bind<bool>("06 - Rendering", "Aggressive Opaque Material Restore", false, RestartRequired("For materials authored as opaque, also restore alpha keywords, blend/ZWrite state, and render queue after Amazing Nature's broad force-alpha pass. Implies the conservative guard and remains opt-in because misclassified alpha textures can render incorrectly.")); ApplyLegacyValue(AggressiveOpaqueMaterialRestore, legacyValues.AggressiveOpaqueMaterialRestore, legacyValues, "Balrond Shaders", "Aggressive Opaque Material Restore"); DisableKnownHeavyPrefabShadows = config.Bind<bool>("06 - Rendering", "Disable Shadows On Audited Heavy Prefabs", false, RestartRequired("Optional visual tradeoff: disable cast/receive shadows on five audited building templates and cast shadows only on FloatingDebris_bal.")); ApplyLegacyValue(DisableKnownHeavyPrefabShadows, legacyValues.DisableKnownHeavyPrefabShadows, legacyValues, "Balrond Rendering", "Disable Shadows On Known Heavy Prefabs"); FixBalrondConstructionsNullHitEffects = config.Bind<bool>("07 - Compatibility Fixes", "Fix Balrond Constructions Null Hit Effects", true, RestartRequired("Remove only enabled missing hit-effect references from five affected Balrond Constructions core-wood wall templates.")); ApplyLegacyValue(FixBalrondConstructionsNullHitEffects, legacyValues.FixBalrondConstructionsNullHitEffects, legacyValues, "Balrond Lifecycle", "Fix Balrond Constructions Null Hit Effects"); SmoothDistantTerrainRebuild = config.Bind<bool>("08 - Vanilla Streaming", "Smooth Distant Terrain Rebuild", true, RestartRequired("Spread Valheim's 3x3 distant-terrain refresh across rendered frames instead of regenerating all nine regions in one frame.")); ApplyLegacyValue(SmoothDistantTerrainRebuild, legacyValues.SmoothDistantTerrainRebuild, legacyValues, "Vanilla Streaming", "Smooth Distant Terrain Rebuild"); DistantTerrainRegionsPerFrame = config.Bind<int>("08 - Vanilla Streaming", "Distant Terrain Regions Per Frame", 1, new ConfigDescription(RestartRequired("Ready distant Heightmap regions regenerated per rendered frame while smoothing is enabled."), (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 9), Array.Empty<object>())); ApplyLegacyValue(DistantTerrainRegionsPerFrame, legacyValues.DistantTerrainRegionsPerFrame, legacyValues, "Vanilla Streaming", "Distant Terrain Regions Per Frame"); CacheVanillaZdoSortComparers = config.Bind<bool>("08 - Vanilla Streaming", "Cache ZDO Sort Comparers", true, RestartRequired("Replace three repeatedly allocated vanilla Comparison<ZDO> delegates with exact cached delegates.")); ApplyLegacyValue(CacheVanillaZdoSortComparers, legacyValues.CacheVanillaZdoSortComparers, legacyValues, "Vanilla Streaming", "Cache ZDO Sort Comparers"); StaticPhysicsRecheckInterval = config.Bind<float>("08 - Vanilla Streaming", "Static Physics Recheck Interval", 1f, new ConfigDescription(RestartRequired("Seconds between ground/solid-height rechecks after a StaticPhysics object becomes eligible. Zero preserves vanilla behavior."), (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 10f), Array.Empty<object>())); ApplyLegacyValue(StaticPhysicsRecheckInterval, legacyValues.StaticPhysicsRecheckInterval, legacyValues, "Vanilla Streaming", "Static Physics Recheck Interval"); TimeSliceTerrainClutterRebuilds = config.Bind<bool>("08 - Vanilla Streaming", "Time-Slice Terrain-Triggered Clutter Rebuilds", true, RestartRequired("Let Valheim's existing ClutterSystem rebuild at most one missing/reset grass patch per rendered frame after terrain changes.")); ApplyLegacyValue(TimeSliceTerrainClutterRebuilds, legacyValues.TimeSliceTerrainClutterRebuilds, legacyValues, "Vanilla Streaming", "Time-Slice Terrain-Triggered Clutter Rebuilds"); } catch { _config = null; _initialized = false; throw; } finally { config.SaveOnConfigSet = saveOnConfigSet; } if (saveOnConfigSet) { config.Save(); } _config.SettingChanged += OnConfigSettingChanged; } private static LegacyValues ReadAndRemoveLegacyConfig(ConfigFile config) { LegacyValues result = new LegacyValues { ExistingDefinitions = CaptureExistingConfigDefinitions(config), Enabled = TakeLegacy(config, "General", "Enable Optimizer", fallback: true), ReplaceStableHashReflection = TakeLegacy(config, "Balrond Status Effects", "Replace StableHash Reflection", fallback: true), EnableServingTrayIntegration = TakeLegacy(config, "Balrond Serving Tray", "Enable Serving Tray Integration", fallback: false), DisableServingTrayAutoRegistration = TakeLegacy(config, "Balrond Serving Tray", "Disable Automatic Consumable Registration", fallback: true), ExcludeHeavyFoodDisplays = TakeLegacy(config, "Balrond Serving Tray", "Exclude Extremely Heavy Food Displays", fallback: true), DisablePoisonGeysers = TakeLegacy(config, "Balrond Swamp", "Disable Poison Geysers", fallback: true), OptimizeEnabledPoisonGeysers = TakeLegacy(config, "Balrond Swamp", "Optimize Poison Geysers When Enabled", fallback: true), GeyserParticleLimit = TakeLegacy(config, "Balrond Swamp", "Geyser Max Particles Per System", 512), GeyserMaxNear = TakeLegacy(config, "Balrond Swamp", "Geyser Spawner Max Near", 4), GeyserMaxTotal = TakeLegacy(config, "Balrond Swamp", "Geyser Spawner Max Total", 12), DisableSwampClutter = TakeLegacy(config, "Balrond Swamp", "Disable Balrond Swamp Clutter", fallback: true), DisableSwampVisualVegetation = TakeLegacy(config, "Balrond Swamp", "Disable Balrond Swamp World Vegetation", fallback: true), DisableSwampResourceVegetation = TakeLegacy(config, "Balrond Swamp", "Disable Balrond Swamp Resource Vegetation", fallback: false), BalrondClutterDensityPercent = TakeLegacy(config, "Balrond Streaming", "Balrond Clutter Density Percent", 50), DisableNonInstancedBalrondClutter = TakeLegacy(config, "Balrond Streaming", "Disable Non-Instanced Balrond Clutter", fallback: true), ConservativeMaterialStateFix = TakeLegacy(config, "Balrond Shaders", "Conservative Material State Fix", fallback: false), AggressiveOpaqueMaterialRestore = TakeLegacy(config, "Balrond Shaders", "Aggressive Opaque Material Restore", fallback: false), DisableKnownHeavyPrefabShadows = TakeLegacy(config, "Balrond Rendering", "Disable Shadows On Known Heavy Prefabs", fallback: false), FixBalrondConstructionsNullHitEffects = TakeLegacy(config, "Balrond Lifecycle", "Fix Balrond Constructions Null Hit Effects", fallback: true), SmoothDistantTerrainRebuild = TakeLegacy(config, "Vanilla Streaming", "Smooth Distant Terrain Rebuild", fallback: true), DistantTerrainRegionsPerFrame = TakeLegacy(config, "Vanilla Streaming", "Distant Terrain Regions Per Frame", 1), CacheVanillaZdoSortComparers = TakeLegacy(config, "Vanilla Streaming", "Cache ZDO Sort Comparers", fallback: true), StaticPhysicsRecheckInterval = TakeLegacy(config, "Vanilla Streaming", "Static Physics Recheck Interval", 1f), TimeSliceTerrainClutterRebuilds = TakeLegacy(config, "Vanilla Streaming", "Time-Slice Terrain-Triggered Clutter Rebuilds", fallback: true) }; RemoveLegacy(config, "Balrond Shaders", "Suppress Duplicate Material Passes", fallback: false); RemoveLegacy(config, "06 - Rendering", "Suppress Duplicate Material Passes", fallback: false); RemoveLegacy(config, "Balrond Hot Paths", "Enable CustomLavaDamagePatch", fallback: false); RemoveLegacy(config, "Balrond Hot Paths", "Enable PlainsMiniHeatPatches", fallback: false); RemoveLegacy(config, "Balrond Hot Paths", "Enable MonsterDoorSensor", fallback: false); RemoveLegacy(config, "Balrond Hot Paths", "Enable MonsterClimbPatch", fallback: false); RemoveLegacy(config, "Balrond Hot Paths", "Enable Smooth Stamina Regen Patch", fallback: false); RemoveLegacy(config, "Balrond Hot Paths", "Replace Deep North Reflection", fallback: true); RemoveLegacy(config, "Balrond Hot Paths", "Optimize Fermenter Awake", fallback: true); RemoveLegacy(config, "Balrond Hot Paths", "Optimize Dverger Aggro Hook", fallback: true); RemoveLegacy(config, "Balrond Bug Fixes", "Fix Biome Message Width", fallback: true); RemoveLegacy(config, "Balrond Status Effects", "Throttle Food Status Hook", fallback: true); RemoveLegacy(config, "Balrond Status Effects", "Throttle Environment Status Hooks", fallback: true); RemoveLegacy(config, "Balrond Status Effects", "Environment Status Interval", 0.25f); RemoveLegacy(config, "Balrond Streaming", "Reduce Iced Tree Wind Update Rate", fallback: true); RemoveLegacy(config, "Balrond Streaming", "Disable Outdoor Stalagmite Material Poll", fallback: true); RemoveLegacy(config, "Balrond Streaming", "Skip Idle Converter Catch-up", fallback: true); RemoveLegacy(config, "Balrond Streaming", "Stagger Converter Updates", fallback: true); RemoveLegacy(config, "Balrond GPU", "Enable Heavy Tree LODs", fallback: true); RemoveLegacy(config, "Balrond GPU", "Heavy Tree Far LOD Screen Height", 0.2f); RemoveLegacy(config, "Balrond GPU", "Heavy Tree Cull Screen Height", 0.02f); RemoveLegacy(config, "Balrond GPU", "Enable Mip-Based Large Texture Reduction", fallback: true); RemoveLegacy(config, "Balrond GPU", "Large Texture Minimum Size", 1024); RemoveLegacy(config, "Balrond GPU", "Large Texture Mip Offset", 1); RemoveLegacy(config, "Balrond Lifecycle", "Fix Recipe Accumulation", fallback: true); RemoveLegacy(config, "Balrond Lifecycle", "Fix Tutorial Accumulation", fallback: true); RemoveLegacy(config, "Balrond Lifecycle", "Fix Tentacle Prefab Lifecycle", fallback: true); return result; } private static HashSet<ConfigDefinition> CaptureExistingConfigDefinitions(ConfigFile config) { HashSet<ConfigDefinition> hashSet = new HashSet<ConfigDefinition>(config.Keys); if (typeof(ConfigFile).GetProperty("OrphanedEntries", BindingFlags.Instance | BindingFlags.NonPublic)?.GetValue(config) is IDictionary dictionary) { foreach (DictionaryEntry item in dictionary) { object key = item.Key; ConfigDefinition val = (ConfigDefinition)((key is ConfigDefinition) ? key : null); if (val != null) { hashSet.Add(val); } } } else { ManualLogSource log = Log; if (log != null) { log.LogWarning((object)"Could not inspect unbound BepInEx config entries; legacy values will not overwrite current 0.7.7 keys."); } } return hashSet; } private static void ApplyLegacyValue<T>(ConfigEntry<T> current, T legacyValue, LegacyValues legacy, string legacySection, string legacyKey) { //IL_0003: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Expected O, but got Unknown ConfigDefinition item = new ConfigDefinition(legacySection, legacyKey); if (legacy.ExistingDefinitions.Contains(item) && !legacy.ExistingDefinitions.Contains(((ConfigEntryBase)current).Definition)) { current.Value = legacyValue; } } private static T TakeLegacy<T>(ConfigFile config, string section, string key, T fallback) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Expected O, but got Unknown ConfigDefinition val = new ConfigDefinition(section, key); try { T value = config.Bind<T>(val, fallback, (ConfigDescription)null).Value; config.Remove(val); return value; } catch (Exception ex) { config.Remove(val); ManualLogSource log = Log; if (log != null) { log.LogDebug((object)("Could not migrate legacy config " + section + "/" + key + ": " + ex.Message)); } return fallback; } } private static void RemoveLegacy<T>(ConfigFile config, string section, string key, T fallback) { TakeLegacy(config, section, key, fallback); } private static string RestartRequired(string description) { return "[Restart required] " + description; } private static void OnConfigSettingChanged(object sender, SettingChangedEventArgs args) { if (_settingsCaptured && !_restartWarningLogged) { _restartWarningLogged = true; object obj; if (args == null) { obj = null; } else { ConfigEntryBase changedSetting = args.ChangedSetting; obj = ((changedSetting == null) ? null : ((object)changedSetting.Definition)?.ToString()); } if (obj == null) { obj = "optimizer setting"; } Warn((string?)obj + " was saved, but runtime patches were not changed. Restart Valheim to apply optimizer settings."); } } private static RuntimeOptions CaptureOptions() { return new RuntimeOptions { Enabled = OptimizerEnabled.Value, DisableMonsterClimbing = DisableMonsterClimbing.Value, ReplaceStableHashReflection = ReplaceStableHashReflection.Value, DisableServingTray = !EnableServingTrayIntegration.Value, DisableServingTrayAutoRegistration = DisableServingTrayAutoRegistration.Value, ExcludeHeavyFoodDisplays = ExcludeHeavyFoodDisplays.Value, DisablePoisonGeysers = DisablePoisonGeysers.Value, OptimizeEnabledPoisonGeysers = OptimizeEnabledPoisonGeysers.Value, GeyserParticleLimit = GeyserParticleLimit.Value, GeyserMaxNear = GeyserMaxNear.Value, GeyserMaxTotal = GeyserMaxTotal.Value, DisableSwampClutter = DisableSwampClutter.Value, DisableSwampVisualVegetation = DisableSwampVisualVegetation.Value, DisableSwampResourceVegetation = DisableSwampResourceVegetation.Value, BalrondClutterDensityPercent = BalrondClutterDensityPercent.Value, DisableNonInstancedBalrondClutter = DisableNonInstancedBalrondClutter.Value, OptimizePortalEffects = OptimizePortalEffects.Value, PortalParticleLimit = PortalParticleLimit.Value, OptimizeFloatingDebrisEffects = OptimizeFloatingDebrisEffects.Value, FloatingDebrisParticleLimit = FloatingDebrisParticleLimit.Value, ConservativeMaterialStateFix = (ConservativeMaterialStateFix.Value || AggressiveOpaqueMaterialRestore.Value), AggressiveOpaqueMaterialRestore = AggressiveOpaqueMaterialRestore.Value, DisableKnownHeavyPrefabShadows = DisableKnownHeavyPrefabShadows.Value, FixBalrondConstructionsNullHitEffects = FixBalrondConstructionsNullHitEffects.Value, SmoothDistantTerrainRebuild = SmoothDistantTerrainRebuild.Value, DistantTerrainRegionsPerFrame = DistantTerrainRegionsPerFrame.Value, CacheVanillaZdoSortComparers = CacheVanillaZdoSortComparers.Value, StaticPhysicsRecheckInterval = StaticPhysicsRecheckInterval.Value, TimeSliceTerrainClutterRebuilds = TimeSliceTerrainClutterRebuilds.Value }; } internal static void Apply() { Options = CaptureOptions(); _settingsCaptured = true; if (!Options.Enabled) { Log.LogInfo((object)"BalrondNatureOptimizer is disabled by its startup master switch. No patches were installed."); return; } if (!ValidateCompatibilityContract(out var capabilityCount, out var failure)) { Log.LogError((object)("Amazing Nature compatibility contract rejected: " + failure + " No optimizer patches were installed.")); return; } StartupIssues.Clear(); _collectStartupIssues = true; TryInstall("Monster climbing", InstallMonsterClimbingControl); TryInstall("StableHash", InstallStableHashFix); TryInstall("Material state", InstallMaterialStateFixes); TryInstall("Serving Tray", InstallServingTrayOptimizations); TryInstall("Vanilla streaming", InstallStreamingOptimizations); TryInstall("World templates", InstallWorldOptimizations); TryInstall("Session cleanup", InstallSessionCleanup); _collectStartupIssues = false; Log.LogInfo((object)("BalrondNatureOptimizer 0.7.7 / " + string.Format("Amazing Nature {0}: capabilities={1}/{2}, startupIssues={3}.", "1.3.8", capabilityCount, capabilityCount, StartupIssues.Count))); if (StartupIssues.Count > 0) { Log.LogWarning((object)("Optimizer startup issues: " + string.Join(" | ", StartupIssues))); } } internal static void Shutdown() { Patcher.UnpatchSelf(); ResetPluginBindings(); if (_config != null) { _config.SettingChanged -= OnConfigSettingChanged; _config = null; } Options = null; _balrondAssembly = null; TypeCache.Clear(); FieldCache.Clear(); StartupIssues.Clear(); _settingsCaptured = false; _restartWarningLogged = false; _collectStartupIssues = false; _initialized = false; } private static bool ValidateCompatibilityContract(out int capabilityCount, out string failure) { capabilityCount = 0; failure = null; if (!Chainloader.PluginInfos.TryGetValue("balrond.astafaraios.BalrondAmazingNature", out var value)) { failure = "plugin GUID balrond.astafaraios.BalrondAmazingNature is not loaded."; return false; } Version version = value.Metadata.Version; if (version == null || version != SupportedBalrondVersionValue) { failure = "expected exactly 1.3.8, found " + (version?.ToString() ?? "unknown") + "."; return false; } _balrondAssembly = ((object)value.Instance)?.GetType().Assembly; if (_balrondAssembly == null) { failure = "the loaded plugin assembly could not be resolved."; return false; } List<string> list = new List<string>(); if (Options.ReplaceStableHashReflection) { RequireMethod(list, "BalrondHashCompat", "StableHash", new Type[1] { typeof(string) }, typeof(int), isStatic: true); } if (Options.DisableMonsterClimbing) { RequireMethod(list, "BalrondNature.CharacterRuntimePatches+Character_CustomFixedUpdate_Patch", "Postfix", new Type[2] { typeof(Character), typeof(float) }, typeof(void), isStatic: true); RequireMethod(list, "BalrondNature.CharacterRuntimePatches+Character_OnDestroy_Patch", "Prefix", new Type[1] { typeof(Character) }, typeof(void), isStatic: true); RequireMonsterClimbingStatesField(list); RequireMethod(list, typeof(Character), "CustomFixedUpdate", new Type[1] { typeof(float) }, typeof(void), isStatic: false); RequireMethod(list, typeof(Character), "OnDestroy", Type.EmptyTypes, typeof(void), isStatic: false); } if (Options.ConservativeMaterialStateFix) { RequireMethod(list, "BalrondNature.ShaderReplacement", "ReplaceShader", new Type[2] { typeof(Material), typeof(string) }, typeof(bool), isStatic: true); RequireMethod(list, "BalrondNature.ShaderReplacement", "ForceAlphaCutoutFix", new Type[1] { typeof(Material) }, typeof(void), isStatic: true); } if (Options.DisableServingTray) { RequireMethod(list, "BalrondNature.ServingTrayBuilder", "SetupBuildPieces", new Type[1] { typeof(List<GameObject>) }, typeof(void), isStatic: false); } else { if (Options.DisableServingTrayAutoRegistration) { RequireMethod(list, "BalrondNature.ServingTrayBuilder", "BuildAutoScanList", Type.EmptyTypes, typeof(List<GameObject>), isStatic: false); } if (Options.DisableServingTrayAutoRegistration || Options.ExcludeHeavyFoodDisplays) { RequireMethod(list, "BalrondNature.ServingTrayBuilder", "TryPrepareAsServingTrayPiece", new Type[7] { typeof(GameObject), typeof(Piece), typeof(WearNTear), typeof(Transform), typeof(PieceCategory), typeof(bool), typeof(string).MakeByRefType() }, typeof(bool), isStatic: true); } } if (Options.DisablePoisonGeysers) { RequireMethod(list, "BalrondNature.MonsterManager", "doSpawnerChanges", new Type[1] { typeof(string) }, typeof(void), isStatic: false); } if (Options.DisablePoisonGeysers || Options.DisableSwampVisualVegetation || Options.DisableSwampResourceVegetation) { RequireField(list, "BalrondNature.VegetationBuilder", "ZoneVegetations", isStatic: true, isReadOnly: false, typeof(List<ZoneVegetation>)); RequireMethod(list, typeof(ZoneSystem), "SetupLocations", Type.EmptyTypes, typeof(void), isStatic: false); } if (Options.DisableSwampClutter || Options.BalrondClutterDensityPercent < 100 || Options.DisableNonInstancedBalrondClutter) { RequireField(list, "BalrondNature.Launch", "clutterBuilder", isStatic: true, isReadOnly: false, null, "BalrondNature.ClutterBuilder"); RequireField(list, "BalrondNature.ClutterBuilder", "clutterObjects", isStatic: false, isReadOnly: true, typeof(List<Clutter>)); RequireMethod(list, typeof(ClutterSystem), "Awake", Type.EmptyTypes, typeof(void), isStatic: false); } if (Options.DisablePoisonGeysers || Options.OptimizeEnabledPoisonGeysers || Options.OptimizePortalEffects || Options.OptimizeFloatingDebrisEffects || Options.DisableKnownHeavyPrefabShadows || Options.FixBalrondConstructionsNullHitEffects) { RequireMethod(list, typeof(ZNetScene), "Awake", Type.EmptyTypes, typeof(void), isStatic: false); } RequireMethod(list, typeof(Game), "Shutdown", new Type[1] { typeof(bool) }, typeof(void), isStatic: false); if (Options.SmoothDistantTerrainRebuild) { RequireMethod(list, typeof(TerrainLod), "RebuildAllHeightmaps", Type.EmptyTypes, typeof(void), isStatic: false); RequireMethod(list, typeof(TerrainLod), "OnEnable", Type.EmptyTypes, typeof(void), isStatic: false); RequireMethod(list, typeof(TerrainLod), "OnDisable", Type.EmptyTypes, typeof(void), isStatic: false); } if (Options.CacheVanillaZdoSortComparers) { Type nestedType = typeof(ZDOMan).GetNestedType("ZDOPeer", BindingFlags.NonPublic); Type[] arguments = new Type[2] { typeof(ZDO), typeof(ZDO) }; RequireMethod(list, typeof(ZNetScene), "CreateObjectsSorted", new Type[3] { typeof(List<ZDO>), typeof(int), typeof(int).MakeByRefType() }, typeof(void), isStatic: false); RequireMethod(list, typeof(ZNetScene), "ZDOCompare", arguments, typeof(int), isStatic: true); if (nestedType == null) { list.Add("ZDOMan.ZDOPeer"); } else { RequireMethod(list, typeof(ZDOMan), "ServerSortSendZDOS", new Type[3] { typeof(List<ZDO>), typeof(Vector3), nestedType }, typeof(void), isStatic: false); RequireMethod(list, typeof(ZDOMan), "ClientSortSendZDOS", new Type[2] { typeof(List<ZDO>), nestedType }, typeof(void), isStatic: false); } RequireMethod(list, typeof(ZDOMan), "ServerSendCompare", arguments, typeof(int), isStatic: true); RequireMethod(list, typeof(ZDOMan), "ClientSendCompare", arguments, typeof(int), isStatic: true); } if (Options.StaticPhysicsRecheckInterval > 0f) { RequireMethod(list, typeof(StaticPhysics), "SUpdate", new Type[2] { typeof(float), typeof(Vector2i) }, typeof(void), isStatic: false); } if (Options.TimeSliceTerrainClutterRebuilds) { RequireMethod(list, typeof(ClutterSystem), "ResetGrass", new Type[2] { typeof(Vector3), typeof(float) }, typeof(void), isStatic: false); } capabilityCount = CountRequestedCapabilities(); if (list.Count == 0) { return true; } failure = string.Format("{0} required member(s) are missing or incompatible: {1}.", list.Count, string.Join(", ", list)); return false; } private static int CountRequestedCapabilities() { return 1 + (Options.ReplaceStableHashReflection ? 1 : 0) + (Options.DisableMonsterClimbing ? 5 : 0) + (Options.ConservativeMaterialStateFix ? 2 : 0) + (Options.DisableServingTray ? 1 : 0) + ((!Options.DisableServingTray && Options.DisableServingTrayAutoRegistration) ? 1 : 0) + ((!Options.DisableServingTray && (Options.DisableServingTrayAutoRegistration || Options.ExcludeHeavyFoodDisplays)) ? 1 : 0) + (Options.DisablePoisonGeysers ? 1 : 0) + ((Options.DisablePoisonGeysers || Options.DisableSwampVisualVegetation || Options.DisableSwampResourceVegetation) ? 2 : 0) + ((Options.DisableSwampClutter || Options.BalrondClutterDensityPercent < 100 || Options.DisableNonInstancedBalrondClutter) ? 3 : 0) + ((Options.DisablePoisonGeysers || Options.OptimizeEnabledPoisonGeysers || Options.OptimizePortalEffects || Options.OptimizeFloatingDebrisEffects || Options.DisableKnownHeavyPrefabShadows || Options.FixBalrondConstructionsNullHitEffects) ? 1 : 0) + (Options.SmoothDistantTerrainRebuild ? 3 : 0) + (Options.CacheVanillaZdoSortComparers ? 6 : 0) + ((Options.StaticPhysicsRecheckInterval > 0f) ? 1 : 0) + (Options.TimeSliceTerrainClutterRebuilds ? 1 : 0); } private static void RequireMethod(List<string> missing, string typeName, string methodName, Type[] arguments, Type returnType, bool isStatic) { MethodInfo methodInfo = FindMethod(FindType(typeName), methodName, arguments); if (methodInfo == null || methodInfo.IsStatic != isStatic || (returnType != null && methodInfo.ReturnType != returnType)) { missing.Add(typeName + "." + methodName); } } private static void RequireMethod(List<string> missing, Type type, string methodName, Type[] arguments, Type returnType, bool isStatic) { MethodInfo methodInfo = FindMethod(type, methodName, arguments); if (methodInfo == null || methodInfo.IsStatic != isStatic || methodInfo.ReturnType != returnType) { missing.Add(type.FullName + "." + methodName); } } private static void RequireField(List<string> missing, string typeName, string fieldName, bool isStatic, bool isReadOnly, Type expectedType = null, string expectedTypeName = null) { FieldInfo fieldInfo = FindField(FindType(typeName), fieldName); if (fieldInfo == null || fieldInfo.IsStatic != isStatic || fieldInfo.IsInitOnly != isReadOnly || (expectedType != null && fieldInfo.FieldType != expectedType) || (expectedTypeName != null && fieldInfo.FieldType.FullName != expectedTypeName)) { missing.Add(typeName + "." + fieldName); } } private static void RequireMonsterClimbingStatesField(List<string> missing) { FieldInfo fieldInfo = FindField(FindType("BalrondNature.CharacterRuntimePatches+MonsterClimbing"), "States"); Type type = fieldInfo?.FieldType; Type[] array = (((object)type != null && type.IsGenericType) ? type.GetGenericArguments() : Type.EmptyTypes); bool flag = (object)type != null && type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Dictionary<, >) && array.Length == 2 && array[0] == typeof(Character) && array[1].FullName == "BalrondNature.CharacterRuntimePatches+MonsterClimbing+ClimbState"; if (fieldInfo == null || !fieldInfo.IsStatic || !fieldInfo.IsInitOnly || !flag) { missing.Add("BalrondNature.CharacterRuntimePatches+MonsterClimbing.States"); } } private static void TryInstall(string feature, Action install) { try { install(); } catch (Exception ex) { Warn(feature + ": " + ex.GetType().Name + ": " + ex.Message); ManualLogSource log = Log; if (log != null) { log.LogDebug((object)ex); } } } private static void InstallMonsterClimbingControl() { if (!Options.DisableMonsterClimbing) { return; } MethodInfo original = FindMethod(typeof(Character), "CustomFixedUpdate", new Type[1] { typeof(float) }); MethodInfo patch = FindMethod(FindType("BalrondNature.CharacterRuntimePatches+Character_CustomFixedUpdate_Patch"), "Postfix", new Type[2] { typeof(Character), typeof(float) }); if (UnpatchExact(original, patch, "Monster Climbing CustomFixedUpdate postfix")) { object obj = FindField(FindType("BalrondNature.CharacterRuntimePatches+MonsterClimbing"), "States")?.GetValue(null); if (!(obj is IDictionary)) { Warn("Monster Climbing update was disabled, but its States dictionary could not be cleared; OnDestroy cleanup remains installed."); return; } int num = ClearCollection(obj); MethodInfo original2 = FindMethod(typeof(Character), "OnDestroy", Type.EmptyTypes); MethodInfo patch2 = FindMethod(FindType("BalrondNature.CharacterRuntimePatches+Character_OnDestroy_Patch"), "Prefix", new Type[1] { typeof(Character) }); bool flag = UnpatchExact(original2, patch2, "Monster Climbing OnDestroy cleanup prefix"); Log.LogInfo((object)$"Monster Climbing disabled; cleared {num} cached state(s), cleanupRemoved={flag}."); } } private static void InstallStableHashFix() { if (Options.ReplaceStableHashReflection) { PatchPrefix(FindType("BalrondHashCompat"), "StableHash", "BalrondStableHashPrefix", new Type[1] { typeof(string) }); } } private static void InstallMaterialStateFixes() { if (Options.ConservativeMaterialStateFix) { Type originalType = FindType("BalrondNature.ShaderReplacement"); Type[] arguments = new Type[2] { typeof(Material), typeof(string) }; Type[] arguments2 = new Type[1] { typeof(Material) }; if (!PatchPrefix(originalType, "ReplaceShader", "ReplaceShaderPrefix", arguments) || !PatchPostfix(originalType, "ReplaceShader", "ReplaceShaderPostfix", arguments) || !PatchPrefix(originalType, "ForceAlphaCutoutFix", "ForceAlphaCutoutFixPrefix", arguments2)) { UnpatchOwn(originalType, "ReplaceShader", "ReplaceShaderPrefix", arguments); UnpatchOwn(originalType, "ReplaceShader", "ReplaceShaderPostfix", arguments); UnpatchOwn(originalType, "ForceAlphaCutoutFix", "ForceAlphaCutoutFixPrefix", arguments2); } } } private static void InstallServingTrayOptimizations() { if (!Options.DisableServingTray && !Options.DisableServingTrayAutoRegistration && !Options.ExcludeHeavyFoodDisplays) { return; } Type originalType = FindType("BalrondNature.ServingTrayBuilder"); if (Options.DisableServingTray) { PatchPrefix(originalType, "SetupBuildPieces", "ServingTraySetupPrefix", new Type[1] { typeof(List<GameObject>) }); return; } if (Options.DisableServingTrayAutoRegistration) { PatchPrefix(originalType, "BuildAutoScanList", "ServingTrayBuildAutoScanListPrefix", Type.EmptyTypes); } if (Options.DisableServingTrayAutoRegistration || Options.ExcludeHeavyFoodDisplays) { PatchPrefix(originalType, "TryPrepareAsServingTrayPiece", "ServingTrayPreparePrefix", new Type[7] { typeof(GameObject), typeof(Piece), typeof(WearNTear), typeof(Transform), typeof(PieceCategory), typeof(bool), typeof(string).MakeByRefType() }); } } private static void InstallWorldOptimizations() { if (Options.DisablePoisonGeysers || Options.OptimizeEnabledPoisonGeysers || Options.OptimizePortalEffects || Options.OptimizeFloatingDebrisEffects || Options.DisableKnownHeavyPrefabShadows || Options.FixBalrondConstructionsNullHitEffects) { PatchPostfixAfterBalrond(typeof(ZNetScene), "Awake", "ZNetSceneAwakePostfix"); } if (Options.DisablePoisonGeysers) { PatchPrefix(FindType("BalrondNature.MonsterManager"), "doSpawnerChanges", "MonsterSpawnerSetupPrefix"); } if (Options.DisablePoisonGeysers || Options.DisableSwampVisualVegetation || Options.DisableSwampResourceVegetation) { PatchPrefixBeforeBalrond(typeof(ZoneSystem), "SetupLocations", "ZoneSystemSetupLocationsPrefix"); PatchPostfixAfterBalrond(typeof(ZoneSystem), "SetupLocations", "ZoneSystemSetupLocationsPostfix"); } if (Options.DisableSwampClutter || Options.BalrondClutterDensityPercent < 100 || Options.DisableNonInstancedBalrondClutter) { PatchPrefixBeforeBalrond(typeof(ClutterSystem), "Awake", "ClutterSystemAwakePrefix"); } } private static void InstallStreamingOptimizations() { if (Options.CacheVanillaZdoSortComparers) { InstallZdoSortComparerCache(); } if (Options.SmoothDistantTerrainRebuild) { InstallTerrainLodSmoothing(); } if (Options.StaticPhysicsRecheckInterval > 0f) { InstallStaticPhysicsCadenceFix(); } if (Options.TimeSliceTerrainClutterRebuilds) { InstallTerrainTriggeredClutterSmoothing(); } } private static void InstallSessionCleanup() { PatchPostfix(typeof(Game), "Shutdown", "GameShutdownPostfix", new Type[1] { typeof(bool) }); } internal static MethodInfo FindMethod(Type type, string name, Type[] arguments = null) { if (type == null) { return null; } if (arguments != null) { return AccessTools.Method(type, name, arguments, (Type[])null); } return AccessTools.Method(type, name, (Type[])null, (Type[])null); } internal static Type FindType(string typeName) { if (string.IsNullOrEmpty(typeName)) { return null; } if (TypeCache.TryGetValue(typeName, out var value)) { return value; } Type type = _balrondAssembly?.GetType(typeName, throwOnError: false, ignoreCase: false); TypeCache[typeName] = type; return type; } internal static FieldInfo FindField(Type type, string fieldName) { if (type == null || string.IsNullOrEmpty(fieldName)) { return null; } if (!FieldCache.TryGetValue(type, out var value)) { value = new Dictionary<string, FieldInfo>(StringComparer.Ordinal); FieldCache[type] = value; } if (value.TryGetValue(fieldName, out var value2)) { return value2; } return value[fieldName] = AccessTools.Field(type, fieldName); } private static bool PatchPrefix(Type originalType, string originalName, string patchName, Type[] arguments = null) { return Patch(originalType, originalName, patchName, prefix: true, arguments, null, null); } private static bool PatchPrefixBeforeBalrond(Type originalType, string originalName, string patchName, Type[] arguments = null) { return Patch(originalType, originalName, patchName, prefix: true, arguments, new string[1] { "balrond.astafaraios.BalrondAmazingNature" }, null); } private static bool PatchPostfix(Type originalType, string originalName, string patchName, Type[] arguments = null) { return Patch(originalType, originalName, patchName, prefix: false, arguments, null, null); } private static bool PatchPostfixAfterBalrond(Type originalType, string originalName, string patchName, Type[] arguments = null) { return Patch(originalType, originalName, patchName, prefix: false, arguments, null, new string[1] { "balrond.astafaraios.BalrondAmazingNature" }); } private static bool PatchTranspiler(Type originalType, string originalName, string patchName, Type[] arguments = null) { //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Expected O, but got Unknown try { MethodInfo methodInfo = FindMethod(originalType, originalName, arguments); MethodInfo methodInfo2 = AccessTools.Method(typeof(OptimizerRuntime), patchName, (Type[])null, (Type[])null); if (methodInfo == null || methodInfo2 == null) { Warn("Transpiler target missing: " + (originalType?.FullName ?? "<null>") + "." + originalName + " <- " + patchName + "."); return false; } Patcher.Patch((MethodBase)methodInfo, (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(methodInfo2), (HarmonyMethod)null, (HarmonyMethod)null); return true; } catch (Exception ex) { Warn("Transpiler target incompatible: " + (originalType?.FullName ?? "<null>") + "." + originalName + " <- " + patchName + ": " + ex.GetType().Name + ": " + ex.Message); return false; } } private static bool Patch(Type originalType, string originalName, string patchName, bool prefix, Type[] arguments, string[] before, string[] after) { //IL_0087: 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_0094: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Expected O, but got Unknown try { MethodInfo methodInfo = FindMethod(originalType, originalName, arguments); MethodInfo methodInfo2 = AccessTools.Method(typeof(OptimizerRuntime), patchName, (Type[])null, (Type[])null); if (methodInfo == null || methodInfo2 == null) { Warn("Patch target missing: " + (originalType?.FullName ?? "<null>") + "." + originalName + " <- " + patchName + "."); return false; } HarmonyMethod val = new HarmonyMethod(methodInfo2) { before = before, after = after }; if (prefix) { Patcher.Patch((MethodBase)methodInfo, val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } else { Patcher.Patch((MethodBase)methodInfo, (HarmonyMethod)null, val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } return true; } catch (Exception ex) { Warn("Patch target incompatible: " + (originalType?.FullName ?? "<null>") + "." + originalName + " <- " + patchName + ": " + ex.GetType().Name + ": " + ex.Message); return false; } } private static bool UnpatchExact(MethodInfo original, MethodInfo patch, string label) { if (original == null || patch == null) { Warn(label + " could not be resolved."); return false; } try { if (!IsHarmonyPatchInstalled(original, patch)) { Warn(label + " was not installed; no upstream patch was removed."); return false; } Patcher.Unpatch((MethodBase)original, patch); if (IsHarmonyPatchInstalled(original, patch)) { Warn(label + " remained installed after the exact unpatch request."); return false; } return true; } catch (Exception ex) { Warn(label + " could not be removed: " + ex.GetType().Name + ": " + ex.Message); return false; } } private static void UnpatchOwn(Type originalType, string originalName, string patchName, Type[] arguments = null) { MethodInfo methodInfo = FindMethod(originalType, originalName, arguments); MethodInfo methodInfo2 = AccessTools.Method(typeof(OptimizerRuntime), patchName, (Type[])null, (Type[])null); if (methodInfo != null && methodInfo2 != null) { Patcher.Unpatch((MethodBase)methodInfo, methodInfo2); } } private static bool IsHarmonyPatchInstalled(MethodInfo original, MethodInfo patchMethod) { Patches patchInfo = Harmony.GetPatchInfo((MethodBase)original); if (patchInfo == null) { return false; } foreach (Patch prefix in patchInfo.Prefixes) { if (prefix.PatchMethod == patchMethod) { return true; } } foreach (Patch postfix in patchInfo.Postfixes) { if (postfix.PatchMethod == patchMethod) { return true; } } foreach (Patch transpiler in patchInfo.Transpilers) { if (transpiler.PatchMethod == patchMethod) { return true; } } foreach (Patch finalizer in patchInfo.Finalizers) { if (finalizer.PatchMethod == patchMethod) { return true; } } return false; } private static bool TryCreateDelegate<T>(MethodInfo method, out T result) where T : class { result = null; if (method == null) { return false; } try { result = Delegate.CreateDelegate(typeof(T), method) as T; return result != null; } catch { return false; } } internal static object GetStaticField(string typeName, string fieldName) { return FindField(FindType(typeName), fieldName)?.GetValue(null); } internal static object GetInstanceField(object instance, string fieldName) { if (instance != null) { return FindField(instance.GetType(), fieldName)?.GetValue(instance); } return null; } internal static int ClearCollection(object collection) { if (collection == null) { return 0; } if (collection is IDictionary { Count: var count } dictionary) { dictionary.Clear(); return count; } if (collection is IList { Count: var count2 } list) { list.Clear(); return count2; } int result = ((collection is ICollection collection2) ? collection2.Count : 0); MethodInfo methodInfo = AccessTools.Method(collection.GetType(), "Clear", (Type[])null, (Type[])null); if ((object)methodInfo != null) { methodInfo.Invoke(collection, null); return result; } return result; } internal static void Warn(string message) { if (_collectStartupIssues) { if (!StartupIssues.Contains(message)) { StartupIssues.Add(message); } return; } ManualLogSource log = Log; if (log != null) { log.LogWarning((object)message); } } private static void InstallZdoSortComparerCache() { InstallZdoSortComparerCache(typeof(ZNetScene), "CreateObjectsSorted", "ZDOCompare", ref _zNetSceneZdoComparison); InstallZdoSortComparerCache(typeof(ZDOMan), "ServerSortSendZDOS", "ServerSendCompare", ref _serverSendZdoComparison); InstallZdoSortComparerCache(typeof(ZDOMan), "ClientSortSendZDOS", "ClientSendCompare", ref _clientSendZdoComparison); } private static void InstallZdoSortComparerCache(Type owner, string sortMethod, string compareMethod, ref Comparison<ZDO> cache) { if (!TryCreateDelegate<Comparison<ZDO>>(FindMethod(owner, compareMethod, new Type[2] { typeof(ZDO), typeof(ZDO) }), out cache)) { Warn("Cached ZDO comparer could not be bound for " + owner.FullName + "." + sortMethod + "; vanilla code was left unchanged."); } else if (!PatchTranspiler(owner, sortMethod, "CacheZdoComparisonTranspiler")) { cache = null; } } private static IEnumerable<CodeInstruction> CacheZdoComparisonTranspiler(IEnumerable<CodeInstruction> instructions, MethodBase __originalMethod) { //IL_016b: Unknown result type (might be due to invalid IL or missing references) //IL_0172: Expected O, but got Unknown List<CodeInstruction> list = new List<CodeInstruction>(instructions); FieldInfo zdoComparisonCacheField = GetZdoComparisonCacheField(__originalMethod); MethodInfo expectedZdoComparison = GetExpectedZdoComparison(__originalMethod); ConstructorInfo objB = AccessTools.Constructor(typeof(Comparison<ZDO>), new Type[2] { typeof(object), typeof(IntPtr) }, false); int num = -1; int num2 = 0; for (int i = 0; i <= list.Count - 3; i++) { if (!(list[i].opcode != OpCodes.Ldnull) && !(list[i + 1].opcode != OpCodes.Ldftn) && object.Equals(list[i + 1].operand as MethodInfo, expectedZdoComparison) && !(list[i + 2].opcode != OpCodes.Newobj) && object.Equals(list[i + 2].operand as ConstructorInfo, objB)) { num = i; num2++; } } if (zdoComparisonCacheField == null || expectedZdoComparison == null || num2 != 1) { Warn("Cached ZDO comparer IL pattern mismatch in " + __originalMethod?.DeclaringType?.FullName + "." + __originalMethod?.Name + "; vanilla allocation was left intact."); return list; } CodeInstruction val = new CodeInstruction(OpCodes.Ldsfld, (object)zdoComparisonCacheField); MoveInstructionMetadata(list[num], val); MoveInstructionMetadata(list[num + 1], val); MoveInstructionMetadata(list[num + 2], val); list.RemoveRange(num, 3); list.Insert(num, val); Log.LogInfo((object)("Cached the exact ZDO sort comparer used by " + __originalMethod.DeclaringType?.Name + "." + __originalMethod.Name + ".")); return list; } private static FieldInfo GetZdoComparisonCacheField(MethodBase original) { if (original?.DeclaringType == typeof(ZNetScene) && original.Name == "CreateObjectsSorted") { return AccessTools.Field(typeof(OptimizerRuntime), "_zNetSceneZdoComparison"); } if (original?.DeclaringType == typeof(ZDOMan) && original.Name == "ServerSortSendZDOS") { return AccessTools.Field(typeof(OptimizerRuntime), "_serverSendZdoComparison"); } if (original?.DeclaringType == typeof(ZDOMan) && original.Name == "ClientSortSendZDOS") { return AccessTools.Field(typeof(OptimizerRuntime), "_clientSendZdoComparison"); } return null; } private static MethodInfo GetExpectedZdoComparison(MethodBase original) { if (original?.DeclaringType == typeof(ZNetScene) && original.Name == "CreateObjectsSorted") { return FindMethod(typeof(ZNetScene), "ZDOCompare", new Type[2] { typeof(ZDO), typeof(ZDO) }); } if (original?.DeclaringType == typeof(ZDOMan) && original.Name == "ServerSortSendZDOS") { return FindMethod(typeof(ZDOMan), "ServerSendCompare", new Type[2] { typeof(ZDO), typeof(ZDO) }); } if (original?.DeclaringType == typeof(ZDOMan) && original.Name == "ClientSortSendZDOS") { return FindMethod(typeof(ZDOMan), "ClientSendCompare", new Type[2] { typeof(ZDO), typeof(ZDO) }); } return null; } private static void InstallStaticPhysicsCadenceFix() { _staticPhysicsRecheckInterval = Options.StaticPhysicsRecheckInterval; if (!PatchTranspiler(typeof(StaticPhysics), "SUpdate", "StaticPhysicsCadenceTranspiler", new Type[2] { typeof(float), typeof(Vector2i) })) { _staticPhysicsRecheckInterval = 0f; } } private static void InstallTerrainLodSmoothing() { Type nestedType = typeof(TerrainLod).GetNestedType("HeightmapWithOffset", BindingFlags.NonPublic); Type nestedType2 = typeof(TerrainLod).GetNestedType("HeightmapState", BindingFlags.NonPublic); _terrainLodHeightmapsField = AccessTools.Field(typeof(TerrainLod), "m_heightmaps"); _terrainLodTopStateField = AccessTools.Field(typeof(TerrainLod), "m_heightmapState"); _terrainEntryHeightmapField = ((nestedType == null) ? null : AccessTools.Field(nestedType, "m_heightmap")); _terrainEntryOffsetField = ((nestedType == null) ? null : AccessTools.Field(nestedType, "m_offset")); _terrainEntryStateField = ((nestedType == null) ? null : AccessTools.Field(nestedType, "m_state")); if (nestedType == null || nestedType2 == null || _terrainLodHeightmapsField == null || _terrainLodTopStateField == null || _terrainEntryHeightmapField == null || _terrainEntryOffsetField == null || _terrainEntryStateField == null) { Warn("TerrainLod private layout did not match the audited game build; distant-terrain smoothing was not installed."); ResetTerrainLodSmoothing(); return; } try { if (Enum.GetUnderlyingType(nestedType2) != typeof(int)) { throw new InvalidOperationException("TerrainLod.HeightmapState no longer uses Int32 storage."); } _terrainReadyState = Convert.ToInt32(Enum.Parse(nestedType2, "ReadyToRebuild")); _terrainDoneState = Convert.ToInt32(Enum.Parse(nestedType2, "Done")); _terrainEntryStateSetter = CreatePrivateEnumSetter<object>(nestedType, _terrainEntryStateField); _terrainTopStateSetter = CreatePrivateEnumSetter<TerrainLod>(typeof(TerrainLod), _terrainLodTopStateField); _heightmapBuildDataRef = AccessTools.FieldRefAccess<Heightmap, HMBuildData>("m_buildData"); _heightmapCornerBiomesRef = AccessTools.FieldRefAccess<Heightmap, Biome[]>("m_cornerBiomes"); } catch (Exception ex) { Warn("TerrainLod field bindings could not be created; distant-terrain smoothing was not installed: " + ex.Message); ResetTerrainLodSmoothing(); return; } if (HasForeignTerrainLodPatch(FindMethod(typeof(TerrainLod), "RebuildAllHeightmaps"))) { Warn("TerrainLod.RebuildAllHeightmaps already has a Harmony patch; distant-terrain smoothing was skipped to avoid a competing streamer."); ResetTerrainLodSmoothing(); return; } bool flag = PatchPrefix(typeof(TerrainLod), "RebuildAllHeightmaps", "TerrainLodRebuildPrefix"); bool flag2 = PatchPostfix(typeof(TerrainLod), "OnEnable", "TerrainLodOnEnablePostfix"); bool flag3 = PatchPostfix(typeof(TerrainLod), "OnDisable", "TerrainLodOnDisablePostfix"); if (!(flag && flag2 && flag3)) { if (flag) { UnpatchOwn(typeof(TerrainLod), "RebuildAllHeightmaps", "TerrainLodRebuildPrefix"); } if (flag2) { UnpatchOwn(typeof(TerrainLod), "OnEnable", "TerrainLodOnEnablePostfix"); } if (flag3) { UnpatchOwn(typeof(TerrainLod), "OnDisable", "TerrainLodOnDisablePostfix"); } Warn("TerrainLod smoothing was rolled back because its lifecycle targets were not all available."); ResetTerrainLodSmoothing(); } } private static void TerrainLodOnEnablePostfix(TerrainLod __instance) { if (!_terrainLodCompatibilityChecked) { _terrainLodCompatibilityChecked = true; if (HasForeignTerrainLodPatch(FindMethod(typeof(TerrainLod), "RebuildAllHeightmaps", Type.EmptyTypes))) { _terrainLodSmoothingFaulted = true; TerrainLodCycles.Clear(); Warn("A later-loaded Harmony patch also targets TerrainLod.RebuildAllHeightmaps; distant-terrain smoothing failed open to vanilla for this session."); } } if ((Object)(object)__instance != (Object)null && !_terrainLodSmoothingFaulted) { PrepareTerrainLodCycle(__instance); } } private static bool HasForeignTerrainLodPatch(MethodInfo rebuild) { Patches val = ((rebuild == null) ? null : Harmony.GetPatchInfo((MethodBase)rebuild)); if (val != null) { if (!HasForeignPatchOwner(val.Prefixes) && !HasForeignPatchOwner(val.Postfixes) && !HasForeignPatchOwner(val.Transpilers)) { return HasForeignPatchOwner(val.Finalizers); } return true; } return false; } private static bool HasForeignPatchOwner(IEnumerable<Patch> patches) { foreach (Patch patch in patches) { if (!string.Equals(patch.owner, "dragonmotion.balrondnatureoptimizer", StringComparison.Ordinal)) { return true; } } return false; } private static void TerrainLodOnDisablePostfix(TerrainLod __instance) { if ((Object)(object)__instance != (Object)null) { TerrainLodCycles.Remove(((Object)__instance).GetInstanceID()); } } private static bool TerrainLodRebuildPrefix(TerrainLod __instance, Vector3 ___m_lastPoint) { //IL_00d9: Unknown result type (might be due to invalid IL or missing references) //IL_00de: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_01ae: Unknown result type (might be due to invalid IL or missing references) //IL_01bb: Unknown result type (might be due to invalid IL or missing references) //IL_01c0: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)__instance == (Object)null) { return true; } if (_terrainLodSmoothingFaulted) { TryRestoreTerrainCycleForVanilla(__instance); return true; } Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null || ((Character)localPlayer).IsTeleporting()) { TryRestoreTerrainCycleForVanilla(__instance); return true; } try { int instanceID = ((Object)__instance).GetInstanceID(); if (!TerrainLodCycles.TryGetValue(instanceID, out var value) && !PrepareTerrainLodCycle(__instance)) { return true; } value = TerrainLodCycles[instanceID]; if (value.Count <= Options.DistantTerrainRegionsPerFrame) { return true; } if (Time.frameCount != _terrainBudgetFrame) { _terrainBudgetFrame = Time.frameCount; _terrainRegionsProcessedThisFrame = 0; } int num = Options.DistantTerrainRegionsPerFrame - _terrainRegionsProcessedThisFrame; if (num <= 0) { return false; } if (!value.Active) { if (!BeginTerrainLodCycle(value, ___m_lastPoint)) { return true; } } else if (value.LastPoint != ___m_lastPoint || value.World != WorldGenerator.instance || value.Source.Count != value.Count) { throw new InvalidOperationException("TerrainLod changed while a sliced rebuild was active."); } int num2 = value.Count - value.Next; int num3 = Mathf.Min(num, num2); for (int i = 0; i < num3; i++) { int num4 = value.Next++; Heightmap val = value.Heightmaps[num4]; HMBuildData val2 = value.Data[num4]; if ((Object)(object)val == (Object)null || val2 == null) { throw new InvalidOperationException($"TerrainLod region {num4} lost its Heightmap/build data."); } _heightmapBuildDataRef.Invoke(val) = val2; _heightmapCornerBiomesRef.Invoke(val) = val2.m_cornerBiomes; ((Component)val).transform.position = value.LastPoint + value.Offsets[num4]; val.Regenerate(); _terrainRegionsProcessedThisFrame++; } if (value.Next >= value.Count) { CompleteTerrainLodCycle(__instance, value); } return false; } catch (Exception arg) { _terrainLodSmoothingFaulted = true; Warn($"Distant-terrain smoothing failed open and is disabled for this session: {arg}"); TryRestoreAllTerrainCyclesForVanilla(); return true; } } private static bool PrepareTerrainLodCycle(TerrainLod terrainLod) { //IL_013d: Unknown result type (might be due to invalid IL or missing references) //IL_0142: Unknown result type (might be due to invalid IL or missing references) if (!(_terrainLodHeightmapsField?.GetValue(terrainLod) is IList { Count: not 0 } list)) { return false; } int instanceID = ((Object)terrainLod).GetInstanceID(); if (!TerrainLodCycles.TryGetValue(instanceID, out var value) || value.Entries.Length != list.Count) { value = new TerrainLodCycle { Entries = new object[list.Count], Heightmaps = (Heightmap[])(object)new Heightmap[list.Count], Offsets = (Vector3[])(object)new Vector3[list.Count], Data = (HMBuildData[])(object)new HMBuildData[list.Count] }; TerrainLodCycles[instanceID] = value; } value.Source = list; value.Owner = terrainLod; value.Count = list.Count; value.Next = 0; value.Active = false; for (int i = 0; i < list.Count; i++) { object obj = list[i]; if (obj == null) { TerrainLodCycles.Remove(instanceID); return false; } object? value2 = _terrainEntryHeightmapField.GetValue(obj); Heightmap val = (Heightmap)((value2 is Heightmap) ? value2 : null); if ((Object)(object)val == (Object)null) { TerrainLodCycles.Remove(instanceID); return false; } value.Entries[i] = obj; value.Heightmaps[i] = val; value.Offsets[i] = (Vector3)_terrainEntryOffsetField.GetValue(obj); value.Data[i] = null; } return true; } private static bool BeginTerrainLodCycle(TerrainLodCycle cycle, Vector3 lastPoint) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //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_004a: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) HeightmapBuilder instance = HeightmapBuilder.instance; WorldGenerator instance2 = WorldGenerator.instance; if (instance == null || instance2 == null) { return false; } cycle.LastPoint = lastPoint; cycle.World = instance2; cycle.Next = 0; cycle.Active = true; for (int i = 0; i < cycle.Count; i++) { Heightmap val = cycle.Heightmaps[i]; Vector3 val2 = lastPoint + cycle.Offsets[i]; cycle.Data[i] = instance.RequestTerrainSync(val2, val.m_width, val.m_scale, val.IsDistantLod, instance2); } return true; } private static void CompleteTerrainLodCycle(TerrainLod terrainLod, TerrainLodCycle cycle) { for (int i = 0; i < cycle.Count; i++) { _terrainEntryStateSetter(cycle.Entries[i], _terrainDoneState); cycle.Data[i] = null; } _terrainTopStateSetter(terrainLod, _terrainDoneState); cycle.Active = false; cycle.Next = 0; } private static void RestoreTerrainCycleForVanilla(TerrainLod terrainLod) { if ((Object)(object)terrainLod == (Object)null || !TerrainLodCycles.TryGetValue(((Object)terrainLod).GetInstanceID(), out var value) || !value.Active) { return; } for (int i = 0; i < value.Count; i++) { Heightmap val = value.Heightmaps[i]; HMBuildData val2 = value.Data[i]; if ((Object)(object)val != (Object)null && val2 != null) { _heightmapBuildDataRef.Invoke(val) = val2; _heightmapCornerBiomesRef.Invoke(val) = val2.m_cornerBiomes; } if (value.Entries[i] != null) { _terrainEntryStateSetter(value.Entries[i], _terrainReadyState); } value.Data[i] = null; } value.Active = false; value.Next = 0; } private static void TryRestoreTerrainCycleForVanilla(TerrainLod terrainLod) { try { RestoreTerrainCycleForVanilla(terrainLod); } catch (Exception ex) { Warn("Could not fully return a sliced TerrainLod cycle to vanilla: " + ex.Message); } } private static void TryRestoreAllTerrainCyclesForVanilla() { foreach (TerrainLodCycle value in TerrainLodCycles.Values) { if ((Object)(object)value.Owner != (Object)null) { TryRestoreTerrainCycleForVanilla(value.Owner); } } } private static Action<TTarget, int> CreatePrivateEnumSetter<TTarget>(Type declaringType, FieldInfo field) { DynamicMethod dynamicMethod = new DynamicMethod("BNO_Set_" + declaringType.Name + "_" + field.Name, typeof(void), new Type[2] { typeof(TTarget), typeof(int) }, typeof(OptimizerRuntime), skipVisibility: true); ILGenerator iLGenerator = dynamicMethod.GetILGenerator(); iLGenerator.Emit(OpCodes.Ldarg_0); if (typeof(TTarget) == typeof(object)) { iLGenerator.Emit(OpCodes.Castclass, declaringType); } iLGenerator.Emit(OpCodes.Ldarg_1); iLGenerator.Emit(OpCodes.Stfld, field); iLGenerator.Emit(OpCodes.Ret); return (Action<TTarget, int>)dynamicMethod.CreateDelegate(typeof(Action<TTarget, int>)); } private static void InstallTerrainTriggeredClutterSmoothing() { PatchTranspiler(typeof(ClutterSystem), "ResetGrass", "TerrainTriggeredClutterTranspiler", new Type[2] { typeof(Vector3), typeof(float) }); } private static IEnumerable<CodeInstruction> TerrainTriggeredClutterTranspiler(IEnumerable<CodeInstruction> instructions, MethodBase __originalMethod) { List<CodeInstruction> list = new List<CodeInstruction>(instructions); FieldInfo fieldInfo = AccessTools.Field(typeof(ClutterSystem), "m_forceRebuild"); int num = -1; int num2 = 0; for (int i = 0; i <= list.Count - 3; i++) { if (list[i].opcode == OpCodes.Ldarg_0 && list[i + 1].opcode == OpCodes.Ldc_I4_1 && list[i + 2].opcode == OpCodes.Stfld && object.Equals(list[i + 2].operand as FieldInfo, fieldInfo)) { num = i; num2++; } } if (fieldInfo == null || num2 != 1) { Warn("Terrain-triggered clutter IL pattern mismatch in " + __originalMethod?.Name + "; vanilla force rebuild was left unchanged."); return list; } if (num + 3 < list.Count) { MoveInstructionMetadata(list[num], list[num + 3]); MoveInstructionMetadata(list[num + 1], list[num + 3]); MoveInstructionMetad