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 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 HeavyServingTrayPrefabs = new HashSet(StringComparer.Ordinal) { "GoatStew_bal", "ShrededMeat_bal", "MeatBalls_bal", "Surstromming_bal" }; private static readonly List EmptyPrefabList = new List(0); private static readonly HashSet SwampClutterPrefabs = new HashSet(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 SwampVisualVegetationPrefabs = new HashSet(StringComparer.Ordinal) { "WetTree1_bal", "WetTree2_bal", "WetTree3_bal", "LinedSwampTreeLarge_bal", "LinedSwampTreeLarge2_bal", "Oak_Swamp_bal", "sapling_Swamp_bal" }; private static readonly HashSet SwampResourceVegetationPrefabs = new HashSet(StringComparer.Ordinal) { "MineRock_Guck_bal", "Pickable_GuckSack_bal" }; private static readonly HashSet KnownHeavyShadowPrefabs = new HashSet(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 OriginalBalrondClutterStates = new Dictionary(); 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 OptimizerEnabled; internal static ConfigEntry DisableMonsterClimbing; internal static ConfigEntry ReplaceStableHashReflection; internal static ConfigEntry EnableServingTrayIntegration; internal static ConfigEntry DisableServingTrayAutoRegistration; internal static ConfigEntry ExcludeHeavyFoodDisplays; internal static ConfigEntry DisablePoisonGeysers; internal static ConfigEntry OptimizeEnabledPoisonGeysers; internal static ConfigEntry GeyserParticleLimit; internal static ConfigEntry GeyserMaxNear; internal static ConfigEntry GeyserMaxTotal; internal static ConfigEntry DisableSwampClutter; internal static ConfigEntry DisableSwampVisualVegetation; internal static ConfigEntry DisableSwampResourceVegetation; internal static ConfigEntry BalrondClutterDensityPercent; internal static ConfigEntry DisableNonInstancedBalrondClutter; internal static ConfigEntry OptimizePortalEffects; internal static ConfigEntry PortalParticleLimit; internal static ConfigEntry OptimizeFloatingDebrisEffects; internal static ConfigEntry FloatingDebrisParticleLimit; internal static ConfigEntry ConservativeMaterialStateFix; internal static ConfigEntry AggressiveOpaqueMaterialRestore; internal static ConfigEntry DisableKnownHeavyPrefabShadows; internal static ConfigEntry FixBalrondConstructionsNullHitEffects; internal static ConfigEntry SmoothDistantTerrainRebuild; internal static ConfigEntry DistantTerrainRegionsPerFrame; internal static ConfigEntry CacheVanillaZdoSortComparers; internal static ConfigEntry StaticPhysicsRecheckInterval; internal static ConfigEntry TimeSliceTerrainClutterRebuilds; private static readonly Dictionary TypeCache = new Dictionary(StringComparer.Ordinal); private static readonly Dictionary> FieldCache = new Dictionary>(); private static readonly List StartupIssues = new List(); 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 _zNetSceneZdoComparison; private static Comparison _serverSendZdoComparison; private static Comparison _clientSendZdoComparison; private static float _staticPhysicsRecheckInterval; private static readonly Dictionary TerrainLodCycles = new Dictionary(); 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 _terrainEntryStateSetter; private static Action _terrainTopStateSetter; private static FieldRef _heightmapBuildDataRef; private static FieldRef _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 __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 list = new List(); 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())?.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() == (Object)null || (Object)(object)prefab.GetComponent() == (Object)null) { return false; } ParticleSystem[] componentsInChildren = prefab.GetComponentsInChildren(true); Transform val = prefab.transform.Find(lightPath); Light val2 = (((Object)(object)val == (Object)null) ? null : ((Component)val).GetComponent()); LightLod val3 = (((Object)(object)val == (Object)null) ? null : ((Component)val).GetComponent()); LightFlicker val4 = (((Object)(object)val == (Object)null) ? null : ((Component)val).GetComponent()); 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() == (Object)null || (Object)(object)prefab.GetComponent() == (Object)null || (Object)(object)prefab.GetComponent() == (Object)null || (Object)(object)prefab.GetComponent() == (Object)null || (Object)(object)prefab.GetComponentInChildren(true) == (Object)null) { return false; } ParticleSystem[] componentsInChildren = prefab.GetComponentsInChildren(true); Transform val = prefab.transform.Find("WaterSurface/vfx_water_surface"); ParticleSystem val2 = (((Object)(object)val == (Object)null) ? null : ((Component)val).GetComponent()); 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(); 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(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(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(true); for (int k = 0; k < componentsInChildren3.Length; k++) { componentsInChildren3[k].shadows = (LightShadows)0; } SpawnArea[] componentsInChildren4 = prefab.GetComponentsInChildren(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(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(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 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 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 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 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 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 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("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("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("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("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("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("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("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("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("04 - Swamp", "Geyser Max Particles Per System", 512, new ConfigDescription(RestartRequired("Maximum particles for each enabled poison-geyser particle system."), (AcceptableValueBase)(object)new AcceptableValueRange(32, 2000), Array.Empty())); ApplyLegacyValue(GeyserParticleLimit, legacyValues.GeyserParticleLimit, legacyValues, "Balrond Swamp", "Geyser Max Particles Per System"); GeyserMaxNear = config.Bind("04 - Swamp", "Geyser Spawner Max Near", 4, new ConfigDescription(RestartRequired("Maximum nearby monsters for PoisonGeyserSpawner_bal."), (AcceptableValueBase)(object)new AcceptableValueRange(0, 20), Array.Empty())); ApplyLegacyValue(GeyserMaxNear, legacyValues.GeyserMaxNear, legacyValues, "Balrond Swamp", "Geyser Spawner Max Near"); GeyserMaxTotal = config.Bind("04 - Swamp", "Geyser Spawner Max Total", 12, new ConfigDescription(RestartRequired("Maximum total monsters for PoisonGeyserSpawner_bal."), (AcceptableValueBase)(object)new AcceptableValueRange(0, 100), Array.Empty())); ApplyLegacyValue(GeyserMaxTotal, legacyValues.GeyserMaxTotal, legacyValues, "Balrond Swamp", "Geyser Spawner Max Total"); DisableSwampClutter = config.Bind("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("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("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("05 - Balrond Clutter", "Balrond Clutter Density Percent", 50, new ConfigDescription(RestartRequired("Scale only Amazing Nature clutter placement attempts."), (AcceptableValueBase)(object)new AcceptableValueRange(0, 100), Array.Empty())); ApplyLegacyValue(BalrondClutterDensityPercent, legacyValues.BalrondClutterDensityPercent, legacyValues, "Balrond Streaming", "Balrond Clutter Density Percent"); DisableNonInstancedBalrondClutter = config.Bind("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("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("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(128, 1000), Array.Empty())); OptimizeFloatingDebrisEffects = config.Bind("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("06 - Rendering", "Floating Debris Max Particles", 256, new ConfigDescription(RestartRequired("Maximum particles for FloatingDebris_bal/WaterSurface/vfx_water_surface."), (AcceptableValueBase)(object)new AcceptableValueRange(64, 1000), Array.Empty())); ConservativeMaterialStateFix = config.Bind("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("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("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("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("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("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(1, 9), Array.Empty())); ApplyLegacyValue(DistantTerrainRegionsPerFrame, legacyValues.DistantTerrainRegionsPerFrame, legacyValues, "Vanilla Streaming", "Distant Terrain Regions Per Frame"); CacheVanillaZdoSortComparers = config.Bind("08 - Vanilla Streaming", "Cache ZDO Sort Comparers", true, RestartRequired("Replace three repeatedly allocated vanilla Comparison delegates with exact cached delegates.")); ApplyLegacyValue(CacheVanillaZdoSortComparers, legacyValues.CacheVanillaZdoSortComparers, legacyValues, "Vanilla Streaming", "Cache ZDO Sort Comparers"); StaticPhysicsRecheckInterval = config.Bind("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(0f, 10f), Array.Empty())); ApplyLegacyValue(StaticPhysicsRecheckInterval, legacyValues.StaticPhysicsRecheckInterval, legacyValues, "Vanilla Streaming", "Static Physics Recheck Interval"); TimeSliceTerrainClutterRebuilds = config.Bind("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 CaptureExistingConfigDefinitions(ConfigFile config) { HashSet hashSet = new HashSet(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(ConfigEntry 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(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(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(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 list = new List(); 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) }, typeof(void), isStatic: false); } else { if (Options.DisableServingTrayAutoRegistration) { RequireMethod(list, "BalrondNature.ServingTrayBuilder", "BuildAutoScanList", Type.EmptyTypes, typeof(List), 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)); 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)); 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), 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), typeof(Vector3), nestedType }, typeof(void), isStatic: false); RequireMethod(list, typeof(ZDOMan), "ClientSortSendZDOS", new Type[2] { typeof(List), 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 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 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 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 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) }); 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(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 ?? "") + "." + 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 ?? "") + "." + 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 ?? "") + "." + 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 ?? "") + "." + 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(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 cache) { if (!TryCreateDelegate>(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 CacheZdoComparisonTranspiler(IEnumerable 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 list = new List(instructions); FieldInfo zdoComparisonCacheField = GetZdoComparisonCacheField(__originalMethod); MethodInfo expectedZdoComparison = GetExpectedZdoComparison(__originalMethod); ConstructorInfo objB = AccessTools.Constructor(typeof(Comparison), 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(nestedType, _terrainEntryStateField); _terrainTopStateSetter = CreatePrivateEnumSetter(typeof(TerrainLod), _terrainLodTopStateField); _heightmapBuildDataRef = AccessTools.FieldRefAccess("m_buildData"); _heightmapCornerBiomesRef = AccessTools.FieldRefAccess("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 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 CreatePrivateEnumSetter(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)dynamicMethod.CreateDelegate(typeof(Action)); } private static void InstallTerrainTriggeredClutterSmoothing() { PatchTranspiler(typeof(ClutterSystem), "ResetGrass", "TerrainTriggeredClutterTranspiler", new Type[2] { typeof(Vector3), typeof(float) }); } private static IEnumerable TerrainTriggeredClutterTranspiler(IEnumerable instructions, MethodBase __originalMethod) { List list = new List(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]); MoveInstructionMetadata(list[num + 2], list[num + 3]); } list.RemoveRange(num, 3); Log.LogInfo((object)"TerrainModifier grass resets now use ClutterSystem's existing one-patch-per-frame generator."); return list; } private static IEnumerable StaticPhysicsCadenceTranspiler(IEnumerable instructions, MethodBase __originalMethod) { //IL_0102: Unknown result type (might be due to invalid IL or missing references) //IL_010c: Expected O, but got Unknown //IL_0113: Unknown result type (might be due to invalid IL or missing references) //IL_011d: Expected O, but got Unknown //IL_0124: Unknown result type (might be due to invalid IL or missing references) //IL_012e: Expected O, but got Unknown //IL_0135: Unknown result type (might be due to invalid IL or missing references) //IL_013f: Expected O, but got Unknown //IL_0146: Unknown result type (might be due to invalid IL or missing references) //IL_0150: Expected O, but got Unknown List list = new List(instructions); FieldInfo objB = AccessTools.Field(typeof(StaticPhysics), "m_fall"); FieldInfo fieldInfo = AccessTools.Field(typeof(StaticPhysics), "m_updateTime"); FieldInfo fieldInfo2 = AccessTools.Field(typeof(OptimizerRuntime), "_staticPhysicsRecheckInterval"); int index = -1; int num = 0; for (int i = 0; i < list.Count - 1; i++) { if (list[i].opcode == OpCodes.Ldarg_0 && list[i + 1].opcode == OpCodes.Ldfld && object.Equals(list[i + 1].operand as FieldInfo, objB)) { index = i; num++; } } if (fieldInfo == null || fieldInfo2 == null || num != 1) { Warn("StaticPhysics cadence IL pattern mismatch in " + __originalMethod?.Name + "; vanilla behavior was left unchanged."); return list; } List list2 = new List { new CodeInstruction(OpCodes.Ldarg_0, (object)null), new CodeInstruction(OpCodes.Ldarg_1, (object)null), new CodeInstruction(OpCodes.Ldsfld, (object)fieldInfo2), new CodeInstruction(OpCodes.Add, (object)null), new CodeInstruction(OpCodes.Stfld, (object)fieldInfo) }; MoveInstructionMetadata(list[index], list2[0]); list[index].labels.Clear(); list[index].blocks.Clear(); list.InsertRange(index, list2); Log.LogInfo((object)$"Installed allocation-free StaticPhysics recheck cadence: {_staticPhysicsRecheckInterval:0.###} s."); return list; } private static void MoveInstructionMetadata(CodeInstruction source, CodeInstruction destination) { if (source.labels.Count > 0) { destination.labels.AddRange(source.labels); } if (source.blocks.Count > 0) { destination.blocks.AddRange(source.blocks); } } private static void ResetStreamingBindings() { _zNetSceneZdoComparison = null; _serverSendZdoComparison = null; _clientSendZdoComparison = null; _staticPhysicsRecheckInterval = 0f; ResetTerrainLodSmoothing(); } private static void ResetStreamingSessionState() { TryRestoreAllTerrainCyclesForVanilla(); TerrainLodCycles.Clear(); _terrainLodCompatibilityChecked = false; _terrainLodSmoothingFaulted = false; _terrainBudgetFrame = -1; _terrainRegionsProcessedThisFrame = 0; } private static void ResetTerrainLodSmoothing() { if (_heightmapBuildDataRef != null && _heightmapCornerBiomesRef != null && _terrainEntryStateSetter != null) { foreach (TerrainLodCycle value in TerrainLodCycles.Values) { if ((Object)(object)value.Owner != (Object)null) { TryRestoreTerrainCycleForVanilla(value.Owner); } } } TerrainLodCycles.Clear(); _terrainLodHeightmapsField = null; _terrainLodTopStateField = null; _terrainEntryHeightmapField = null; _terrainEntryOffsetField = null; _terrainEntryStateField = null; _terrainReadyState = 0; _terrainDoneState = 0; _terrainEntryStateSetter = null; _terrainTopStateSetter = null; _heightmapBuildDataRef = null; _heightmapCornerBiomesRef = null; _terrainLodCompatibilityChecked = false; _terrainLodSmoothingFaulted = false; _terrainBudgetFrame = -1; _terrainRegionsProcessedThisFrame = 0; } }