Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of SakeysRootRemover v0.0.1
BepInEx/plugins/Sakey's Roort Remover/Sakey's Roort Remover.dll
Decompiled 7 hours agousing 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; } } }