Decompiled source of SakeysRootRemover v0.0.1

BepInEx/plugins/Sakey's Roort Remover/Sakey's Roort Remover.dll

Decompiled 7 hours ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Threading;
using BepInEx;
using BepInEx.Configuration;
using HarmonyLib;
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("Sakey's Roort Remover")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("Sakey's Roort Remover")]
[assembly: AssemblyTitle("Sakey's Roort Remover")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace SakeysRootRemover;

[BepInPlugin("com.sakey.rootremover", "Sakey's Root Remover", "0.0.1")]
public sealed class RootRemoverPlugin : BaseUnityPlugin
{
	public const string PluginGuid = "com.sakey.rootremover";

	public const string PluginName = "Sakey's Root Remover";

	public const string PluginVersion = "0.0.1";

	internal const string TargetAltBiome = "Root Black Forest";

	internal const string TargetPrefab = "Greydwarf_Root";

	internal const string TargetBrokenPrefab = "Greydwarf_Root_frac";

	internal const string TargetWildEnemyPrefab = "TentaRoot_wild";

	internal const string TargetAspectEnemyPrefab = "Aspect_TentaRoot";

	internal const string TargetBlackForestBiome = "BlackForest";

	internal const string TargetSwampBiome = "Swamp";

	internal const string TargetDeepNorthBiome = "DeepNorth";

	private static readonly string[] TargetZdoPrefabNames = new string[4] { "Greydwarf_Root", "Greydwarf_Root_frac", "TentaRoot_wild", "Aspect_TentaRoot" };

	private static readonly string[] TargetVegetationPrefabNames = new string[2] { "Greydwarf_Root", "Greydwarf_Root_frac" };

	internal static RootRemoverPlugin Instance;

	private static readonly object ReflectionCacheLock = new object();

	private static readonly Dictionary<Type, Dictionary<string, MemberInfo>> MemberCache = new Dictionary<Type, Dictionary<string, MemberInfo>>();

	private static readonly Dictionary<Tuple<Type, Type>, MethodInfo> BiomeSectorMethodCache = new Dictionary<Tuple<Type, Type>, MethodInfo>();

	private static readonly Dictionary<Type, MethodInfo> BaseBiomeMethodCache = new Dictionary<Type, MethodInfo>();

	private ConfigEntry<bool> _removeExisting;

	private ConfigEntry<bool> _preventNewVegetation;

	private ConfigEntry<bool> _preventWildRootSpawning;

	private ConfigEntry<float> _rescanInterval;

	private ConfigEntry<bool> _verboseLogging;

	private Harmony _harmony;

	private object _activeZdoMan;

	private object _cachedWorldGenerator;

	private float _nextWorldCheckAt;

	private float _nextWorldGeneratorRefreshAt;

	private float _nextBlockedSpawnSummaryAt;

	private float _nextScanAt;

	private int _blockedWildSpawnCount;

	private int _blockedAspectSpawnCount;

	private bool _reportedMissingPrefab;

	private bool _reportedReflectionFailure;

	private int _scanCount;

	internal bool PreventNewVegetation
	{
		get
		{
			if (_preventNewVegetation != null)
			{
				return _preventNewVegetation.Value;
			}
			return false;
		}
	}

	internal bool PreventWildRootSpawning
	{
		get
		{
			if (_preventWildRootSpawning != null)
			{
				return _preventWildRootSpawning.Value;
			}
			return false;
		}
	}

	internal bool VerboseLogging
	{
		get
		{
			if (_verboseLogging != null)
			{
				return _verboseLogging.Value;
			}
			return false;
		}
	}

	internal object CachedWorldGenerator => _cachedWorldGenerator;

	internal unsafe void RecordBlockedSpawn(string prefabName, string regionName, Vector3 spawnPoint, bool eventSpawner)
	{
		//IL_0032: Unknown result type (might be due to invalid IL or missing references)
		//IL_0033: Unknown result type (might be due to invalid IL or missing references)
		if (VerboseLogging)
		{
			string[] obj = new string[9] { "Blocked ", prefabName, " spawn in ", regionName, " at ", null, null, null, null };
			Vector3 val = spawnPoint;
			obj[5] = ((object)(*(Vector3*)(&val))/*cast due to .constrained prefix*/).ToString();
			obj[6] = " (eventSpawner=";
			obj[7] = eventSpawner.ToString();
			obj[8] = ").";
			LogInfo(string.Concat(obj));
		}
		else if (string.Equals(prefabName, "TentaRoot_wild", StringComparison.Ordinal))
		{
			Interlocked.Increment(ref _blockedWildSpawnCount);
		}
		else if (string.Equals(prefabName, "Aspect_TentaRoot", StringComparison.Ordinal))
		{
			Interlocked.Increment(ref _blockedAspectSpawnCount);
		}
	}

	private void ReportBlockedSpawnSummary(float now)
	{
		if (_nextBlockedSpawnSummaryAt <= 0f)
		{
			_nextBlockedSpawnSummaryAt = now + 60f;
		}
		else if (!(now < _nextBlockedSpawnSummaryAt))
		{
			_nextBlockedSpawnSummaryAt = now + 60f;
			int num = Interlocked.Exchange(ref _blockedWildSpawnCount, 0);
			int num2 = Interlocked.Exchange(ref _blockedAspectSpawnCount, 0);
			if (num > 0 || num2 > 0)
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)("Blocked spawn attempts during the last 60 seconds: TentaRoot_wild=" + num + " (BlackForest / Swamp), Aspect_TentaRoot=" + num2 + " (DeepNorth). Per-spawn logging is suppressed; enable Logging.VerboseLogging for individual coordinates."));
			}
		}
	}

	internal void LogInfo(string message)
	{
		((BaseUnityPlugin)this).Logger.LogInfo((object)message);
	}

	internal void LogWarning(string message)
	{
		((BaseUnityPlugin)this).Logger.LogWarning((object)message);
	}

	internal void LogError(string message)
	{
		((BaseUnityPlugin)this).Logger.LogError((object)message);
	}

	private void Awake()
	{
		//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
		//IL_00bf: Expected O, but got Unknown
		Instance = this;
		_removeExisting = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "RemoveExistingRoots", true, "Remove Greydwarf_Root in Root Black Forest, TentaRoot_wild in BlackForest and Swamp, and Aspect_TentaRoot in DeepNorth.");
		_preventNewVegetation = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "PreventNewRootVegetation", true, "Prevent Greydwarf_Root vegetation from being placed in newly generated Root Black Forest zones.");
		_preventWildRootSpawning = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "PreventWildTentaRootSpawning", true, "Block TentaRoot_wild SpawnSystem spawns in BlackForest and Swamp, and Aspect_TentaRoot spawns in DeepNorth. TentaRoot (Elder summon) is untouched.");
		_rescanInterval = ((BaseUnityPlugin)this).Config.Bind<float>("General", "RescanIntervalSeconds", 120f, "How often existing generated Root Black Forest objects are checked. Minimum: 60 seconds to avoid frequent full-world ZDO scans.");
		_verboseLogging = ((BaseUnityPlugin)this).Config.Bind<bool>("Logging", "VerboseLogging", false, "Write per-object details to the BepInEx log.");
		_harmony = new Harmony("com.sakey.rootremover");
		_harmony.PatchAll(typeof(RootRemoverPlugin).Assembly);
		int targetMethodCount = PlaceVegetationPatch.GetTargetMethodCount();
		if (PreventNewVegetation && targetMethodCount == 0)
		{
			((BaseUnityPlugin)this).Logger.LogWarning((object)"Could not locate ZoneSystem.PlaceVegetation(Vector2s, Vector3, ...). Greydwarf_Root vegetation prevention is not active; existing-object cleanup can still work.");
		}
		else if (PreventNewVegetation)
		{
			((BaseUnityPlugin)this).Logger.LogInfo((object)("Greydwarf_Root vegetation prevention patched " + targetMethodCount + " ZoneSystem.PlaceVegetation method(s)."));
		}
		int targetMethodCount2 = RootVariantSpawnPatch.GetTargetMethodCount();
		if (PreventWildRootSpawning && targetMethodCount2 == 0)
		{
			((BaseUnityPlugin)this).Logger.LogWarning((object)"Could not locate SpawnSystem.Spawn(SpawnData, Vector3, bool). TentaRoot_wild / Aspect_TentaRoot spawn prevention is NOT active.");
		}
		else if (PreventWildRootSpawning)
		{
			((BaseUnityPlugin)this).Logger.LogInfo((object)("Root-variant spawn prevention patched " + targetMethodCount2 + " SpawnSystem.Spawn method(s): TentaRoot_wild (BlackForest / Swamp), Aspect_TentaRoot (DeepNorth)."));
		}
		((BaseUnityPlugin)this).Logger.LogInfo((object)"Sakey's Root Remover 0.0.1 loaded. Targets: Greydwarf_Root vegetation in Root Black Forest; TentaRoot_wild in BlackForest / Swamp; Aspect_TentaRoot in DeepNorth. TentaRoot (Elder summon) and Greydwarf_Surprise are excluded.");
	}

	private void Update()
	{
		float realtimeSinceStartup = Time.realtimeSinceStartup;
		if (realtimeSinceStartup >= _nextWorldGeneratorRefreshAt)
		{
			_cachedWorldGenerator = GetSingleton("WorldGenerator");
			_nextWorldGeneratorRefreshAt = realtimeSinceStartup + 1f;
		}
		ReportBlockedSpawnSummary(realtimeSinceStartup);
		if (_removeExisting == null || !_removeExisting.Value || realtimeSinceStartup < _nextWorldCheckAt)
		{
			return;
		}
		_nextWorldCheckAt = realtimeSinceStartup + 1f;
		object singleton = GetSingleton("ZNet");
		object singleton2 = GetSingleton("ZDOMan");
		object singleton3 = GetSingleton("ZNetScene");
		object singleton4 = GetSingleton("ZoneSystem");
		object cachedWorldGenerator = _cachedWorldGenerator;
		if (singleton == null || singleton2 == null || singleton3 == null || singleton4 == null || cachedWorldGenerator == null)
		{
			_activeZdoMan = null;
			return;
		}
		if (!IsServer(singleton))
		{
			_activeZdoMan = null;
			return;
		}
		if (_activeZdoMan != singleton2)
		{
			_activeZdoMan = singleton2;
			_nextScanAt = 0f;
			((BaseUnityPlugin)this).Logger.LogInfo((object)"World systems detected. Initial Root Black Forest cleanup is ready.");
		}
		if (realtimeSinceStartup < _nextScanAt)
		{
			return;
		}
		float num = ((_rescanInterval == null) ? 120f : Mathf.Max(60f, _rescanInterval.Value));
		_nextScanAt = realtimeSinceStartup + num;
		try
		{
			ScanAndRemoveExistingRoots(singleton2, singleton3, cachedWorldGenerator);
		}
		catch (Exception ex)
		{
			((BaseUnityPlugin)this).Logger.LogError((object)("Existing-root scan failed safely: " + ex));
		}
	}

	private void ScanAndRemoveExistingRoots(object zdoMan, object znetScene, object worldGenerator)
	{
		if (!(GetMemberValue(zdoMan, "m_objectsByID") is IDictionary dictionary))
		{
			LogReflectionFailure("ZDOMan.m_objectsByID was not available as a dictionary. Existing-root cleanup is skipped.");
			return;
		}
		if (!(GetMemberValue(znetScene, "m_namedPrefabs") is IDictionary namedPrefabs))
		{
			LogReflectionFailure("ZNetScene.m_namedPrefabs was not available as a dictionary. Existing-root cleanup is skipped.");
			return;
		}
		HashSet<int> hashSet = ResolveTargetPrefabHashes(namedPrefabs);
		if (hashSet.Count == 0)
		{
			if (!_reportedMissingPrefab)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)"None of the configured target prefabs (Greydwarf_Root, TentaRoot_wild, Aspect_TentaRoot) were found in ZNetScene.m_namedPrefabs. No objects were deleted.");
				_reportedMissingPrefab = true;
			}
			return;
		}
		List<object> list = new List<object>();
		IDictionaryEnumerator enumerator = dictionary.GetEnumerator();
		while (enumerator.MoveNext())
		{
			object value = enumerator.Value;
			if (value == null)
			{
				continue;
			}
			object memberValue = GetMemberValue(value, "m_prefab");
			if (memberValue != null)
			{
				int item;
				try
				{
					item = Convert.ToInt32(memberValue);
				}
				catch
				{
					continue;
				}
				if (hashSet.Contains(item))
				{
					list.Add(value);
				}
			}
		}
		int num = 0;
		int num2 = 0;
		int num3 = 0;
		foreach (object item2 in list)
		{
			object obj2 = InvokeNoArgs(item2, "GetPosition");
			string prefabName = GetPrefabName(namedPrefabs, item2);
			if (obj2 == null || !ShouldRemovePrefabAtPosition(prefabName, worldGenerator, obj2))
			{
				num2++;
			}
			else if (RemoveSingleZdo(item2, zdoMan, znetScene))
			{
				num++;
				if (VerboseLogging)
				{
					((BaseUnityPlugin)this).Logger.LogInfo((object)("Removed " + prefabName + " at " + obj2?.ToString() + " (matching configured biome)."));
				}
			}
			else
			{
				num3++;
			}
		}
		_scanCount++;
		if (num > 0 || num3 > 0 || VerboseLogging || _scanCount == 1)
		{
			((BaseUnityPlugin)this).Logger.LogInfo((object)("Root cleanup scan #" + _scanCount + ": candidates=" + list.Count + ", removed=" + num + ", outside configured target biomes=" + num2 + ", failed=" + num3 + "."));
		}
	}

	private HashSet<int> ResolveTargetPrefabHashes(IDictionary namedPrefabs)
	{
		HashSet<int> hashSet = new HashSet<int>();
		IDictionaryEnumerator enumerator = namedPrefabs.GetEnumerator();
		while (enumerator.MoveNext())
		{
			string unityObjectName = GetUnityObjectName(enumerator.Value);
			if (!string.IsNullOrEmpty(unityObjectName) && TargetZdoPrefabNames.Contains<string>(unityObjectName, StringComparer.Ordinal))
			{
				try
				{
					hashSet.Add(Convert.ToInt32(enumerator.Key));
				}
				catch
				{
				}
			}
		}
		return hashSet;
	}

	private static string GetPrefabName(IDictionary namedPrefabs, object zdo)
	{
		try
		{
			int num = Convert.ToInt32(GetMemberValue(zdo, "m_prefab"));
			return GetUnityObjectName(namedPrefabs[num]) ?? ("hash:" + num);
		}
		catch
		{
			return "unknown-prefab";
		}
	}

	private bool RemoveSingleZdo(object zdo, object zdoMan, object znetScene)
	{
		try
		{
			object obj = InvokeNoArgs(zdo, "IsValid");
			if (!(obj is bool) || !(bool)obj)
			{
				return false;
			}
			MethodInfo methodInfo = FindMethod(zdoMan.GetType(), "GetSessionID", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, 0);
			MethodInfo methodInfo2 = FindMethod(zdo.GetType(), "SetOwnerInternal", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, 1);
			if (methodInfo == null || methodInfo2 == null)
			{
				LogReflectionFailure("Could not resolve ZDOMan.GetSessionID or ZDO.SetOwnerInternal. The object was left untouched.");
				return false;
			}
			object obj2 = methodInfo.Invoke(null, null);
			if (obj2 == null)
			{
				LogReflectionFailure("ZDOMan.GetSessionID returned null. The object was left untouched.");
				return false;
			}
			methodInfo2.Invoke(zdo, new object[1] { obj2 });
			if (GetMemberValue(znetScene, "m_instances") is IDictionary dictionary && dictionary.Contains(zdo))
			{
				object memberValue = GetMemberValue(dictionary[zdo], "gameObject");
				if (memberValue == null)
				{
					LogReflectionFailure("Loaded ZDO had no GameObject in ZNetScene.m_instances. The object was left untouched.");
					return false;
				}
				MethodInfo methodInfo3 = FindGameObjectDestroyMethod(znetScene.GetType(), memberValue.GetType());
				if (methodInfo3 == null)
				{
					LogReflectionFailure("Could not resolve ZNetScene.Destroy(GameObject). The loaded object was left untouched.");
					return false;
				}
				methodInfo3.Invoke(znetScene, new object[1] { memberValue });
				return true;
			}
			MethodInfo methodInfo4 = FindMethod(zdoMan.GetType(), "DestroyZDO", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, 1);
			if (methodInfo4 == null)
			{
				LogReflectionFailure("Could not resolve ZDOMan.DestroyZDO(ZDO). The unloaded object was left untouched.");
				return false;
			}
			methodInfo4.Invoke(zdoMan, new object[1] { zdo });
			return true;
		}
		catch (Exception ex)
		{
			((BaseUnityPlugin)this).Logger.LogWarning((object)("Could not remove one target ZDO; it was not retried in this scan. " + ex.GetBaseException().Message));
			return false;
		}
	}

	private static bool ShouldRemovePrefabAtPosition(string prefabName, object worldGenerator, object position)
	{
		if (string.Equals(prefabName, "Greydwarf_Root", StringComparison.Ordinal) || string.Equals(prefabName, "Greydwarf_Root_frac", StringComparison.Ordinal))
		{
			return IsInRootAltBiome(worldGenerator, position);
		}
		if (string.Equals(prefabName, "TentaRoot_wild", StringComparison.Ordinal))
		{
			if (!IsInBaseBiome(worldGenerator, position, "BlackForest") && !IsInBaseBiome(worldGenerator, position, "Swamp"))
			{
				return IsInRootAltBiome(worldGenerator, position);
			}
			return true;
		}
		if (string.Equals(prefabName, "Aspect_TentaRoot", StringComparison.Ordinal))
		{
			return IsInBaseBiome(worldGenerator, position, "DeepNorth");
		}
		return false;
	}

	internal static string GetSpawnBlockRegion(string prefabName, object worldGenerator, Vector3 position)
	{
		//IL_000f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0065: Unknown result type (might be due to invalid IL or missing references)
		//IL_0028: Unknown result type (might be due to invalid IL or missing references)
		//IL_0041: Unknown result type (might be due to invalid IL or missing references)
		if (string.Equals(prefabName, "TentaRoot_wild", StringComparison.Ordinal))
		{
			if (IsInBaseBiome(worldGenerator, position, "BlackForest"))
			{
				return "BlackForest";
			}
			if (IsInBaseBiome(worldGenerator, position, "Swamp"))
			{
				return "Swamp";
			}
			if (IsInRootAltBiome(worldGenerator, position))
			{
				return "Root Black Forest";
			}
			return null;
		}
		if (string.Equals(prefabName, "Aspect_TentaRoot", StringComparison.Ordinal) && IsInBaseBiome(worldGenerator, position, "DeepNorth"))
		{
			return "DeepNorth";
		}
		return null;
	}

	internal static bool IsInBaseBiome(object worldGenerator, object position, string biomeName)
	{
		//IL_0012: Unknown result type (might be due to invalid IL or missing references)
		//IL_0017: Unknown result type (might be due to invalid IL or missing references)
		//IL_0054: 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 (worldGenerator == null || position == null || string.IsNullOrEmpty(biomeName))
		{
			return false;
		}
		try
		{
			Vector3 val = (Vector3)position;
			MethodInfo methodInfo = FindBaseBiomeMethod(worldGenerator.GetType());
			if (methodInfo == null)
			{
				Instance?.LogReflectionFailure("Could not resolve WorldGenerator.GetBiome(float, float, float, bool). Base-biome filtering (BlackForest / Swamp / DeepNorth) is unavailable and fails closed.");
				return false;
			}
			object obj = methodInfo.Invoke(worldGenerator, new object[4] { val.x, val.z, 0.02f, false });
			if (obj == null)
			{
				return false;
			}
			Type returnType = methodInfo.ReturnType;
			if (returnType.IsEnum)
			{
				object value = Enum.Parse(returnType, biomeName, ignoreCase: true);
				long num = Convert.ToInt64(obj);
				long num2 = Convert.ToInt64(value);
				return num2 != 0L && (num & num2) == num2;
			}
			return string.Equals(Convert.ToString(obj), biomeName, StringComparison.OrdinalIgnoreCase);
		}
		catch (Exception ex)
		{
			Instance?.LogReflectionFailure("Base-biome lookup for " + biomeName + " failed closed: " + ex.GetBaseException().Message);
			return false;
		}
	}

	private static MethodInfo FindBaseBiomeMethod(Type worldGeneratorType)
	{
		lock (ReflectionCacheLock)
		{
			if (BaseBiomeMethodCache.TryGetValue(worldGeneratorType, out var value))
			{
				return value;
			}
			MethodInfo methodInfo = worldGeneratorType.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault(delegate(MethodInfo method)
			{
				if (method.Name != "GetBiome" || !method.ReturnType.IsEnum)
				{
					return false;
				}
				ParameterInfo[] parameters = method.GetParameters();
				return parameters.Length == 4 && parameters[0].ParameterType == typeof(float) && parameters[1].ParameterType == typeof(float) && parameters[2].ParameterType == typeof(float) && parameters[3].ParameterType == typeof(bool);
			});
			BaseBiomeMethodCache[worldGeneratorType] = methodInfo;
			return methodInfo;
		}
	}

	internal static bool IsInRootAltBiome(object worldGenerator, object position)
	{
		try
		{
			MethodInfo methodInfo = FindBiomeSectorMethod(worldGenerator.GetType(), position.GetType());
			if (methodInfo == null)
			{
				Instance?.LogReflectionFailure("Could not resolve WorldGenerator.GetBiomeSector(Vector3, bool). Biome filtering failed closed.");
				return false;
			}
			object obj = methodInfo.Invoke(worldGenerator, new object[2] { position, false });
			if (obj == null)
			{
				return false;
			}
			if (!(GetMemberValue(obj, "AltBiomes") is IEnumerable enumerable))
			{
				return false;
			}
			foreach (object item in enumerable)
			{
				if (string.Equals(GetMemberValue(item, "m_name") as string, "Root Black Forest", StringComparison.OrdinalIgnoreCase))
				{
					return true;
				}
			}
		}
		catch (Exception ex)
		{
			Instance?.LogReflectionFailure("Alt-biome lookup failed closed: " + ex.GetBaseException().Message);
		}
		return false;
	}

	private static MethodInfo FindBiomeSectorMethod(Type worldGeneratorType, Type positionType)
	{
		Tuple<Type, Type> key = Tuple.Create(worldGeneratorType, positionType);
		lock (ReflectionCacheLock)
		{
			if (BiomeSectorMethodCache.TryGetValue(key, out var value))
			{
				return value;
			}
			MethodInfo methodInfo = worldGeneratorType.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault(delegate(MethodInfo method)
			{
				if (method.Name != "GetBiomeSector")
				{
					return false;
				}
				ParameterInfo[] parameters = method.GetParameters();
				return parameters.Length == 2 && parameters[0].ParameterType == positionType && parameters[1].ParameterType == typeof(bool);
			});
			BiomeSectorMethodCache[key] = methodInfo;
			return methodInfo;
		}
	}

	private static bool IsServer(object znet)
	{
		try
		{
			object obj = InvokeNoArgs(znet, "IsServer");
			return obj is bool && (bool)obj;
		}
		catch
		{
			return false;
		}
	}

	internal static object GetSingleton(string typeName)
	{
		Type type = AccessTools.TypeByName(typeName);
		if (type == null)
		{
			return null;
		}
		FieldInfo field = type.GetField("instance", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
		if (field != null)
		{
			return field.GetValue(null);
		}
		PropertyInfo property = type.GetProperty("instance", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
		if (!(property == null))
		{
			return property.GetValue(null, null);
		}
		return null;
	}

	internal static object GetMemberValue(object instance, string name)
	{
		if (instance == null)
		{
			return null;
		}
		Type type = instance.GetType();
		MemberInfo value2;
		lock (ReflectionCacheLock)
		{
			if (!MemberCache.TryGetValue(type, out var value))
			{
				value = new Dictionary<string, MemberInfo>(StringComparer.Ordinal);
				MemberCache[type] = value;
			}
			if (!value.TryGetValue(name, out value2))
			{
				object obj = ((object)type.GetField(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)) ?? ((object)type.GetProperty(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic));
				value2 = (value[name] = (MemberInfo)obj);
			}
		}
		FieldInfo fieldInfo = value2 as FieldInfo;
		if (fieldInfo != null)
		{
			return fieldInfo.GetValue(instance);
		}
		PropertyInfo propertyInfo = value2 as PropertyInfo;
		if (propertyInfo != null && propertyInfo.GetIndexParameters().Length == 0)
		{
			return propertyInfo.GetValue(instance, null);
		}
		return null;
	}

	internal static object InvokeNoArgs(object instance, string methodName)
	{
		if (instance == null)
		{
			return null;
		}
		MethodInfo methodInfo = FindMethod(instance.GetType(), methodName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, 0);
		if (!(methodInfo == null))
		{
			return methodInfo.Invoke(instance, null);
		}
		return null;
	}

	internal static string GetUnityObjectName(object unityObject)
	{
		if (unityObject == null)
		{
			return null;
		}
		return GetMemberValue(unityObject, "name") as string;
	}

	internal static bool IsTargetVegetationPrefab(string prefabName)
	{
		if (!string.IsNullOrEmpty(prefabName))
		{
			return TargetVegetationPrefabNames.Contains<string>(prefabName, StringComparer.Ordinal);
		}
		return false;
	}

	internal static MethodInfo FindMethod(Type type, string name, BindingFlags flags, int parameterCount)
	{
		return type.GetMethods(flags).FirstOrDefault((MethodInfo method) => method.Name == name && method.GetParameters().Length == parameterCount);
	}

	private static MethodInfo FindGameObjectDestroyMethod(Type sceneType, Type gameObjectType)
	{
		return sceneType.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault(delegate(MethodInfo method)
		{
			if (method.Name != "Destroy")
			{
				return false;
			}
			ParameterInfo[] parameters = method.GetParameters();
			return parameters.Length == 1 && parameters[0].ParameterType.IsAssignableFrom(gameObjectType);
		});
	}

	internal void LogReflectionFailure(string message)
	{
		if (!_reportedReflectionFailure)
		{
			_reportedReflectionFailure = true;
			((BaseUnityPlugin)this).Logger.LogWarning((object)message);
		}
	}

	private void OnDestroy()
	{
		if (_harmony != null)
		{
			_harmony.UnpatchSelf();
		}
		if (Instance == this)
		{
			Instance = null;
		}
	}
}
[HarmonyPatch]
internal static class PlaceVegetationPatch
{
	private sealed class PatchState
	{
		internal object OriginalList;

		internal FieldInfo VegetationField;

		internal object Instance;
	}

	internal static int GetTargetMethodCount()
	{
		return GetTargetMethods().Count();
	}

	private static IEnumerable<MethodBase> TargetMethods()
	{
		return GetTargetMethods().Cast<MethodBase>();
	}

	private static IEnumerable<MethodInfo> GetTargetMethods()
	{
		Type type = AccessTools.TypeByName("ZoneSystem");
		if (type == null)
		{
			return Enumerable.Empty<MethodInfo>();
		}
		return type.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).Where(delegate(MethodInfo method)
		{
			if (method.Name != "PlaceVegetation")
			{
				return false;
			}
			ParameterInfo[] parameters = method.GetParameters();
			return parameters.Length >= 2 && parameters[0].ParameterType.Name == "Vector2s" && parameters[1].ParameterType.FullName == "UnityEngine.Vector3";
		}).ToArray();
	}

	private static void Prefix(object __instance, object[] __args, ref object __state)
	{
		RootRemoverPlugin instance = RootRemoverPlugin.Instance;
		if ((Object)(object)instance == (Object)null || !instance.PreventNewVegetation || __instance == null || __args == null || __args.Length < 2)
		{
			return;
		}
		try
		{
			object singleton = RootRemoverPlugin.GetSingleton("WorldGenerator");
			if (singleton == null || !RootRemoverPlugin.IsInRootAltBiome(singleton, __args[1]))
			{
				return;
			}
			FieldInfo fieldInfo = AccessTools.Field(__instance.GetType(), "m_vegetation");
			if (fieldInfo == null)
			{
				instance.LogReflectionFailure("ZoneSystem.m_vegetation was not found. New-root prevention is inactive.");
				return;
			}
			object value = fieldInfo.GetValue(__instance);
			if (!(value is IList list))
			{
				instance.LogReflectionFailure("ZoneSystem.m_vegetation was not an IList. New-root prevention is inactive.");
				return;
			}
			IList list2 = CreateListLike(list, fieldInfo.FieldType);
			int num = 0;
			foreach (object item in list)
			{
				if (RootRemoverPlugin.IsTargetVegetationPrefab(RootRemoverPlugin.GetUnityObjectName(RootRemoverPlugin.GetMemberValue(item, "m_prefab"))))
				{
					num++;
				}
				else
				{
					list2.Add(item);
				}
			}
			if (num != 0)
			{
				__state = new PatchState
				{
					OriginalList = value,
					VegetationField = fieldInfo,
					Instance = __instance
				};
				fieldInfo.SetValue(__instance, list2);
				if (instance.VerboseLogging)
				{
					instance.LogInfo("Temporarily excluded " + num + " Greydwarf_Root vegetation entry/entries while generating Root Black Forest.");
				}
			}
		}
		catch (Exception ex)
		{
			instance.LogWarning("New-root prevention Prefix failed safely; vanilla vegetation list was retained. " + ex.GetBaseException().Message);
			__state = null;
		}
	}

	private static Exception Finalizer(object __instance, object __state, Exception __exception)
	{
		if (!(__state is PatchState patchState))
		{
			return __exception;
		}
		try
		{
			patchState.VegetationField.SetValue(patchState.Instance, patchState.OriginalList);
		}
		catch (Exception ex)
		{
			RootRemoverPlugin.Instance?.LogError("Could not restore ZoneSystem.m_vegetation after placement. " + ex.GetBaseException().Message);
		}
		return __exception;
	}

	private static IList CreateListLike(IList source, Type listType)
	{
		try
		{
			return (IList)Activator.CreateInstance(listType);
		}
		catch
		{
			Type type = (listType.IsGenericType ? listType.GetGenericArguments()[0] : typeof(object));
			return (IList)Activator.CreateInstance(typeof(List<>).MakeGenericType(type));
		}
	}
}
[HarmonyPatch]
internal static class RootVariantSpawnPatch
{
	internal static int GetTargetMethodCount()
	{
		return GetTargetMethods().Count();
	}

	private static IEnumerable<MethodBase> TargetMethods()
	{
		return GetTargetMethods().Cast<MethodBase>();
	}

	private static IEnumerable<MethodInfo> GetTargetMethods()
	{
		Type type = AccessTools.TypeByName("SpawnSystem");
		if (type == null)
		{
			return Enumerable.Empty<MethodInfo>();
		}
		return type.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).Where(delegate(MethodInfo method)
		{
			if (method.Name != "Spawn")
			{
				return false;
			}
			ParameterInfo[] parameters = method.GetParameters();
			return parameters.Length == 3 && parameters[0].ParameterType.Name == "SpawnData" && parameters[1].ParameterType == typeof(Vector3) && parameters[2].ParameterType == typeof(bool);
		}).ToArray();
	}

	[HarmonyPrefix]
	private static bool Prefix(object critter, Vector3 spawnPoint, bool eventSpawner)
	{
		//IL_0060: 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)
		RootRemoverPlugin instance = RootRemoverPlugin.Instance;
		if ((Object)(object)instance == (Object)null || !instance.PreventWildRootSpawning || critter == null)
		{
			return true;
		}
		try
		{
			string unityObjectName = RootRemoverPlugin.GetUnityObjectName(RootRemoverPlugin.GetMemberValue(critter, "m_prefab"));
			if (!string.Equals(unityObjectName, "TentaRoot_wild", StringComparison.Ordinal) && !string.Equals(unityObjectName, "Aspect_TentaRoot", StringComparison.Ordinal))
			{
				return true;
			}
			object cachedWorldGenerator = instance.CachedWorldGenerator;
			if (cachedWorldGenerator == null)
			{
				return true;
			}
			string spawnBlockRegion = RootRemoverPlugin.GetSpawnBlockRegion(unityObjectName, cachedWorldGenerator, spawnPoint);
			if (string.IsNullOrEmpty(spawnBlockRegion))
			{
				return true;
			}
			instance.RecordBlockedSpawn(unityObjectName, spawnBlockRegion, spawnPoint, eventSpawner);
			return false;
		}
		catch (Exception ex)
		{
			instance.LogWarning("TentaRoot variant spawn filter failed safely; original spawn was allowed. " + ex.GetBaseException().Message);
			return true;
		}
	}
}