Decompiled source of PerformantGrass v1.0.0

PerformantGrass.dll

Decompiled 4 days ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyCompany("PerformantGrass")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("PerformantGrass")]
[assembly: AssemblyTitle("PerformantGrass")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace PerformantGrass
{
	[BepInPlugin("com.dreich.performantgrass", "Performant Grass", "1.0.0")]
	public class PerformantGrass : BaseUnityPlugin
	{
		public const string Guid = "com.dreich.performantgrass";

		public const string Name = "Performant Grass";

		public const string Version = "1.0.0";

		internal static ManualLogSource Log;

		internal static ConfigEntry<bool> Enabled;

		internal static ConfigEntry<int> MaxCachedPatches;

		internal static ConfigEntry<float> GrassDistance;

		internal static ConfigEntry<bool> LogStats;

		private float nextStats;

		private void Awake()
		{
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Expected O, but got Unknown
			//IL_0093: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: Expected O, but got Unknown
			//IL_00f0: Unknown result type (might be due to invalid IL or missing references)
			Log = ((BaseUnityPlugin)this).Logger;
			Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Remember where grass was placed so walking back into an area doesn't have to work it all out again.");
			MaxCachedPatches = ((BaseUnityPlugin)this).Config.Bind<int>("General", "MaxCachedPatches", 1500, new ConfigDescription("How many 8x8 m grass patches to remember. Each one is a few kilobytes; the oldest are forgotten first.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(100, 20000), Array.Empty<object>()));
			GrassDistance = ((BaseUnityPlugin)this).Config.Bind<float>("General", "GrassDistance", 0f, new ConfigDescription("How far away grass is drawn, in meters. 0 = the game's own distance (40). Higher values show more grass but cost more: the number of grass patches grows with the square of the distance.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 150f), Array.Empty<object>()));
			GrassDistance.SettingChanged += delegate
			{
				GrassCache.ApplyDistance(ClutterSystem.instance, rebuild: true);
			};
			LogStats = ((BaseUnityPlugin)this).Config.Bind<bool>("Diagnostics", "LogStats", false, "Every 30 seconds, write how many grass patches were rebuilt from memory and how many had to be worked out from scratch.");
			new Harmony("com.dreich.performantgrass").PatchAll();
		}

		private void Update()
		{
			if (LogStats.Value && !(Time.time < nextStats))
			{
				nextStats = Time.time + 30f;
				GrassCache.ReportStats();
			}
		}
	}
	internal static class GrassCache
	{
		private sealed class InstancedPart
		{
			public GameObject Prefab;

			public Matrix4x4[] Matrices;
		}

		private sealed class LoosePart
		{
			public GameObject Prefab;

			public Vector3 Position;

			public Quaternion Rotation;
		}

		private sealed class CachedPatch
		{
			public Vector2Int Id;

			public Vector3 Center;

			public Quality Quality;

			public float AmountScale;

			public readonly List<InstancedPart> Instanced = new List<InstancedPart>();

			public readonly List<LoosePart> Loose = new List<LoosePart>();

			public LinkedListNode<Vector2Int> Node;
		}

		private static readonly Type PatchDataType = AccessTools.Inner(typeof(ClutterSystem), "PatchData");

		private static readonly FieldInfo PatchCenter = AccessTools.Field(PatchDataType, "center");

		private static readonly FieldInfo PatchObjects = AccessTools.Field(PatchDataType, "m_objects");

		private static readonly MethodInfo AllocatePatch = AccessTools.Method(typeof(ClutterSystem), "AllocatePatch", (Type[])null, (Type[])null);

		private static readonly FieldRef<ClutterSystem, Quality> QualityField = AccessTools.FieldRefAccess<ClutterSystem, Quality>("m_quality");

		private static readonly FieldRef<ClutterSystem, GameObject> GrassRootField = AccessTools.FieldRefAccess<ClutterSystem, GameObject>("m_grassRoot");

		private static readonly FieldRef<InstanceRenderer, Matrix4x4[]> InstancesField = AccessTools.FieldRefAccess<InstanceRenderer, Matrix4x4[]>("m_instances");

		private static readonly FieldRef<InstanceRenderer, int> InstanceCountField = AccessTools.FieldRefAccess<InstanceRenderer, int>("m_instanceCount");

		private static readonly Dictionary<Vector2Int, CachedPatch> cache = new Dictionary<Vector2Int, CachedPatch>();

		private static readonly LinkedList<Vector2Int> order = new LinkedList<Vector2Int>();

		private static readonly Dictionary<string, GameObject> prefabsByName = new Dictionary<string, GameObject>();

		private static ClutterSystem prefabsFor;

		private static int fromCache;

		private static int fromScratch;

		internal static bool WasReplayed;

		private static float gameDistance = -1f;

		internal static bool Ready
		{
			get
			{
				if (PatchDataType != null && PatchCenter != null && PatchObjects != null)
				{
					return AllocatePatch != null;
				}
				return false;
			}
		}

		internal static void ApplyDistance(ClutterSystem system, bool rebuild)
		{
			if ((Object)(object)system == (Object)null || system.m_menuHack)
			{
				return;
			}
			if (gameDistance < 0f)
			{
				gameDistance = system.m_distance;
			}
			float num = ((PerformantGrass.GrassDistance.Value > 0f) ? Mathf.Max(10f, PerformantGrass.GrassDistance.Value) : gameDistance);
			if (!Mathf.Approximately(system.m_distance, num))
			{
				system.m_distance = num;
				if (rebuild)
				{
					system.ClearAll();
				}
			}
		}

		internal static void AdjustLod(ClutterSystem system, object patchData)
		{
			if (patchData == null || PatchObjects == null || !(PatchObjects.GetValue(patchData) is List<GameObject> list))
			{
				return;
			}
			float num = ((gameDistance > 0f) ? (system.m_distance / gameDistance) : 1f);
			float num2 = system.m_distance - system.m_grassPatchSize / 2f;
			foreach (GameObject item in list)
			{
				InstanceRenderer val = (((Object)(object)item != (Object)null) ? item.GetComponent<InstanceRenderer>() : null);
				if (!((Object)(object)val == (Object)null) && val.m_useLod)
				{
					GameObject val2 = PrefabFor(system, item);
					InstanceRenderer val3 = (((Object)(object)val2 != (Object)null) ? val2.GetComponent<InstanceRenderer>() : null);
					if (!((Object)(object)val3 == (Object)null))
					{
						val.m_lodMinDistance = val3.m_lodMinDistance * num;
						val.m_lodMaxDistance = Mathf.Min(val3.m_lodMaxDistance * num, num2);
					}
				}
			}
		}

		internal static void Clear()
		{
			cache.Clear();
			order.Clear();
			prefabsByName.Clear();
			prefabsFor = null;
		}

		internal static bool Has(ClutterSystem system, Vector2Int id)
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			CachedPatch patch;
			if (PerformantGrass.Enabled.Value && Ready)
			{
				return TryGet(system, id, out patch);
			}
			return false;
		}

		private static bool TryGet(ClutterSystem system, Vector2Int id, out CachedPatch patch)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Invalid comparison between Unknown and I4
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			if (!cache.TryGetValue(id, out patch))
			{
				return false;
			}
			if ((int)patch.Quality == (int)QualityField.Invoke(system) && Mathf.Approximately(patch.AmountScale, system.m_amountScale))
			{
				return true;
			}
			Forget(id);
			patch = null;
			return false;
		}

		private static void Forget(Vector2Int id)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			if (cache.TryGetValue(id, out var value))
			{
				order.Remove(value.Node);
				cache.Remove(id);
			}
		}

		internal static void Invalidate(ClutterSystem system, Vector3 center, float radius)
		{
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
			float num = system.m_grassPatchSize / 2f;
			List<Vector2Int> list = new List<Vector2Int>();
			foreach (CachedPatch value in cache.Values)
			{
				Vector3 center2 = value.Center;
				if (!(center2.x + num < center.x - radius) && !(center2.x - num > center.x + radius) && !(center2.z + num < center.z - radius) && !(center2.z - num > center.z + radius))
				{
					list.Add(value.Id);
				}
			}
			foreach (Vector2Int item in list)
			{
				Forget(item);
			}
		}

		internal static object Replay(ClutterSystem system, Vector2Int id)
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_0103: Unknown result type (might be due to invalid IL or missing references)
			//IL_0108: Unknown result type (might be due to invalid IL or missing references)
			//IL_010c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0176: Unknown result type (might be due to invalid IL or missing references)
			//IL_017d: Unknown result type (might be due to invalid IL or missing references)
			if (!PerformantGrass.Enabled.Value || !Ready || !TryGet(system, id, out var patch))
			{
				return null;
			}
			GameObject val = GrassRootField.Invoke(system);
			if ((Object)(object)val == (Object)null)
			{
				return null;
			}
			object obj = AllocatePatch.Invoke(system, null);
			PatchCenter.SetValue(obj, patch.Center);
			List<GameObject> list = (List<GameObject>)PatchObjects.GetValue(obj);
			float num = system.m_distance - system.m_grassPatchSize / 2f;
			foreach (InstancedPart item in patch.Instanced)
			{
				if ((Object)(object)item.Prefab == (Object)null)
				{
					continue;
				}
				GameObject val2 = Object.Instantiate<GameObject>(item.Prefab, patch.Center, Quaternion.identity, val.transform);
				InstanceRenderer component = val2.GetComponent<InstanceRenderer>();
				if ((Object)(object)component != (Object)null)
				{
					if (component.m_lodMaxDistance > num)
					{
						component.m_lodMaxDistance = num;
					}
					Matrix4x4[] matrices = item.Matrices;
					foreach (Matrix4x4 val3 in matrices)
					{
						component.AddInstance(val3);
					}
				}
				list.Add(val2);
			}
			foreach (LoosePart item2 in patch.Loose)
			{
				if (!((Object)(object)item2.Prefab == (Object)null))
				{
					list.Add(Object.Instantiate<GameObject>(item2.Prefab, item2.Position, item2.Rotation, val.transform));
				}
			}
			order.Remove(patch.Node);
			order.AddLast(patch.Node);
			fromCache++;
			return obj;
		}

		internal static void Capture(ClutterSystem system, Vector2Int id, object patchData)
		{
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0177: Unknown result type (might be due to invalid IL or missing references)
			//IL_0183: Unknown result type (might be due to invalid IL or missing references)
			//IL_0193: Unknown result type (might be due to invalid IL or missing references)
			//IL_013a: Unknown result type (might be due to invalid IL or missing references)
			//IL_013f: Unknown result type (might be due to invalid IL or missing references)
			//IL_014c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0151: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b1: Unknown result type (might be due to invalid IL or missing references)
			fromScratch++;
			if (!PerformantGrass.Enabled.Value || !Ready || patchData == null || system.m_menuHack || !(PatchObjects.GetValue(patchData) is List<GameObject> list))
			{
				return;
			}
			CachedPatch cachedPatch = new CachedPatch
			{
				Id = id,
				Center = (Vector3)PatchCenter.GetValue(patchData),
				Quality = QualityField.Invoke(system),
				AmountScale = system.m_amountScale
			};
			foreach (GameObject item in list)
			{
				if (!((Object)(object)item == (Object)null))
				{
					GameObject val = PrefabFor(system, item);
					if ((Object)(object)val == (Object)null)
					{
						return;
					}
					InstanceRenderer component = item.GetComponent<InstanceRenderer>();
					if ((Object)(object)component != (Object)null)
					{
						int num = InstanceCountField.Invoke(component);
						Matrix4x4[] array = (Matrix4x4[])(object)new Matrix4x4[num];
						Array.Copy(InstancesField.Invoke(component), array, num);
						cachedPatch.Instanced.Add(new InstancedPart
						{
							Prefab = val,
							Matrices = array
						});
					}
					else
					{
						cachedPatch.Loose.Add(new LoosePart
						{
							Prefab = val,
							Position = item.transform.position,
							Rotation = item.transform.rotation
						});
					}
				}
			}
			Forget(id);
			cachedPatch.Node = order.AddLast(id);
			cache[id] = cachedPatch;
			int value = PerformantGrass.MaxCachedPatches.Value;
			while (cache.Count > value && order.First != null)
			{
				Forget(order.First.Value);
			}
		}

		private static GameObject PrefabFor(ClutterSystem system, GameObject instance)
		{
			if ((Object)(object)prefabsFor != (Object)(object)system)
			{
				prefabsByName.Clear();
				prefabsFor = system;
				foreach (Clutter item in system.m_clutter)
				{
					if ((Object)(object)item.m_prefab != (Object)null)
					{
						prefabsByName[((Object)item.m_prefab).name] = item.m_prefab;
					}
				}
			}
			string text = ((Object)instance).name;
			int num = text.IndexOf("(Clone)", StringComparison.Ordinal);
			if (num >= 0)
			{
				text = text.Substring(0, num);
			}
			if (!prefabsByName.TryGetValue(text.Trim(), out var value))
			{
				return null;
			}
			return value;
		}

		internal static void ReportStats()
		{
			PerformantGrass.Log.LogInfo((object)("Grass patches in the last 30 s: " + fromCache + " rebuilt from memory, " + fromScratch + " worked out from scratch. Patches remembered: " + cache.Count + "."));
			fromCache = 0;
			fromScratch = 0;
		}
	}
	[HarmonyPatch(typeof(ClutterSystem), "Awake")]
	internal static class ClutterAwakePatch
	{
		private static void Postfix(ClutterSystem __instance)
		{
			GrassCache.Clear();
			if (!__instance.m_menuHack)
			{
				GrassCache.ApplyDistance(__instance, rebuild: false);
			}
			if (!GrassCache.Ready)
			{
				PerformantGrass.Log.LogWarning((object)"The grass system's internals have changed; grass caching is turned off.");
			}
		}
	}
	[HarmonyPatch(typeof(ClutterSystem), "OnDestroy")]
	internal static class ClutterDestroyPatch
	{
		private static void Postfix()
		{
			GrassCache.Clear();
		}
	}
	[HarmonyPatch(typeof(ClutterSystem), "ApplySettings")]
	internal static class ClutterSettingsPatch
	{
		private static void Postfix()
		{
			GrassCache.Clear();
		}
	}
	[HarmonyPatch(typeof(ClutterSystem), "ResetGrass")]
	internal static class ClutterResetPatch
	{
		private static void Prefix(ClutterSystem __instance, Vector3 center, float radius)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			GrassCache.Invalidate(__instance, center, radius);
		}
	}
	[HarmonyPatch(typeof(ClutterSystem), "GeneratePatch")]
	internal static class ClutterGeneratePatchPatch
	{
		private static void Prefix(ClutterSystem __instance, Vector2Int p, ref bool generated, out bool __state)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			__state = generated;
			if (GrassCache.Has(__instance, p))
			{
				generated = false;
			}
		}

		private static void Postfix(ref bool generated, bool __state)
		{
			if (GrassCache.WasReplayed)
			{
				generated = __state;
			}
			GrassCache.WasReplayed = false;
		}
	}
	[HarmonyPatch(typeof(ClutterSystem), "GenerateVegPatch")]
	internal static class ClutterGenerateVegPatchPatch
	{
		private static bool Prefix(ClutterSystem __instance, Vector2Int patchID, ref object __result, out bool __state)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			object obj = GrassCache.Replay(__instance, patchID);
			__state = obj != null;
			GrassCache.WasReplayed = __state;
			if (!__state)
			{
				return true;
			}
			__result = obj;
			return false;
		}

		private static void Postfix(ClutterSystem __instance, Vector2Int patchID, object __result, bool __state)
		{
			//IL_0004: Unknown result type (might be due to invalid IL or missing references)
			if (!__state)
			{
				GrassCache.Capture(__instance, patchID, __result);
			}
			if (!__instance.m_menuHack)
			{
				GrassCache.AdjustLod(__instance, __result);
			}
		}
	}
}