Decompiled source of LootFloat v0.1.0

BepInEx\plugins\LootFloat\LootFloat.dll

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

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyVersion("0.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 LootFloat
{
	[BepInPlugin("com.littlegoblinstudio.lootfloat", "Loot Float", "0.1.0")]
	public sealed class LootFloatPlugin : BaseUnityPlugin
	{
		public const string PluginGuid = "com.littlegoblinstudio.lootfloat";

		public const string PluginName = "Loot Float";

		public const string PluginVersion = "0.1.0";

		private Harmony harmony;

		private void Awake()
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Expected O, but got Unknown
			harmony = new Harmony("com.littlegoblinstudio.lootfloat");
			ObjectDbDetectionPatch.TryRegister(harmony, ((BaseUnityPlugin)this).Logger);
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Loot Float 0.1.0 loaded");
		}

		private void OnDestroy()
		{
			if (harmony != null)
			{
				harmony.UnpatchSelf();
				harmony = null;
			}
		}
	}
	[HarmonyPatch]
	internal static class ObjectDbDetectionPatch
	{
		public static MethodBase TargetMethod()
		{
			try
			{
				Type type = AccessTools.TypeByName("ObjectDB");
				return (type == null) ? null : AccessTools.Method(type, "Awake", (Type[])null, (Type[])null);
			}
			catch
			{
				return null;
			}
		}

		public static bool TryRegister(Harmony harmony, ManualLogSource logger)
		{
			//IL_01ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d1: Expected O, but got Unknown
			//IL_019f: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a6: Expected O, but got Unknown
			//IL_0208: Unknown result type (might be due to invalid IL or missing references)
			//IL_0217: Expected O, but got Unknown
			Type type = null;
			MethodBase methodBase = null;
			try
			{
				type = AccessTools.TypeByName("ObjectDB");
			}
			catch
			{
				type = null;
			}
			logger.LogInfo((object)("ObjectDB type resolved: " + FormatBoolean(type != null)));
			if (type != null)
			{
				logger.LogInfo((object)("ObjectDB type: " + type.FullName));
				logger.LogInfo((object)("ObjectDB assembly: " + type.Assembly.FullName));
				try
				{
					methodBase = AccessTools.Method(type, "Awake", (Type[])null, (Type[])null);
				}
				catch
				{
					methodBase = null;
				}
			}
			logger.LogInfo((object)("ObjectDB Awake resolved: " + FormatBoolean(methodBase != null)));
			if (methodBase != null)
			{
				logger.LogInfo((object)("ObjectDB Awake method: " + methodBase));
			}
			FieldInfo fieldInfo = null;
			if (type != null)
			{
				try
				{
					fieldInfo = AccessTools.Field(type, "m_items");
				}
				catch
				{
					fieldInfo = null;
				}
			}
			logger.LogInfo((object)("ObjectDB m_items field resolved: " + FormatBoolean(fieldInfo != null)));
			if (fieldInfo != null)
			{
				logger.LogInfo((object)("ObjectDB m_items field type: " + fieldInfo.FieldType.FullName));
			}
			MethodBase methodBase2 = null;
			if (type != null)
			{
				try
				{
					methodBase2 = AccessTools.Method(type, "CopyOtherDB", (Type[])null, (Type[])null);
				}
				catch
				{
					methodBase2 = null;
				}
			}
			logger.LogInfo((object)("ObjectDB CopyOtherDB resolved: " + FormatBoolean(methodBase2 != null)));
			if (methodBase2 != null)
			{
				logger.LogInfo((object)("ObjectDB CopyOtherDB method: " + methodBase2));
			}
			HarmonyMethod val = null;
			if (fieldInfo != null)
			{
				try
				{
					val = new HarmonyMethod(typeof(ObjectDbItemCountProbe), "Postfix", (Type[])null);
				}
				catch
				{
					val = null;
				}
			}
			HarmonyMethod val2 = null;
			if (methodBase2 != null)
			{
				try
				{
					val2 = new HarmonyMethod(typeof(ObjectDbItemCountProbe), "WorldLoadedPostfix", (Type[])null);
				}
				catch
				{
					val2 = null;
				}
			}
			bool flag = false;
			bool value = false;
			if (methodBase != null && harmony != null)
			{
				try
				{
					if (val != null)
					{
						ObjectDbItemCountProbe.Configure(fieldInfo, logger);
					}
					harmony.Patch(methodBase, new HarmonyMethod(typeof(ObjectDbDetectionPatch), "NoOpPrefix", (Type[])null), val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
					flag = true;
					value = val != null;
				}
				catch
				{
					ObjectDbItemCountProbe.Clear();
					flag = false;
				}
			}
			bool value2 = false;
			if (methodBase2 != null && val2 != null && harmony != null)
			{
				try
				{
					harmony.Patch(methodBase2, (HarmonyMethod)null, val2, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
					value2 = true;
				}
				catch
				{
					value2 = false;
				}
			}
			logger.LogInfo((object)("Reflection probe patch registered: " + FormatBoolean(flag)));
			logger.LogInfo((object)("ObjectDB item count postfix registered: " + FormatBoolean(value)));
			logger.LogInfo((object)("World-loaded item count postfix registered: " + FormatBoolean(value2)));
			return flag;
		}

		private static bool NoOpPrefix()
		{
			return true;
		}

		private static string FormatBoolean(bool value)
		{
			if (!value)
			{
				return "false";
			}
			return "true";
		}
	}
	internal static class ObjectDbItemCountProbe
	{
		private static FieldInfo itemsField;

		private static ManualLogSource logger;

		private static int earlyProbeLogged;

		private static int worldZeroLogged;

		private static int worldPositiveLogged;

		public static void Configure(FieldInfo field, ManualLogSource log)
		{
			itemsField = field;
			logger = log;
		}

		public static void Clear()
		{
			itemsField = null;
			logger = null;
		}

		public static void Postfix(object __instance)
		{
			if (earlyProbeLogged == 0)
			{
				earlyProbeLogged = 1;
				ManualLogSource currentLogger = logger;
				bool collectionResolved;
				int count;
				bool flag = TryReadCount(__instance, currentLogger, out collectionResolved, out count);
				LogInfo(currentLogger, "ObjectDB early item collection resolved: " + FormatBoolean(collectionResolved));
				LogInfo(currentLogger, "Early potential item prefabs found: " + count);
				LogInfo(currentLogger, "ObjectDB ready at early Awake: " + FormatBoolean(flag && count > 0));
			}
		}

		public static void WorldLoadedPostfix(object __instance)
		{
			if (worldPositiveLogged == 0)
			{
				ManualLogSource currentLogger = logger;
				bool collectionResolved;
				int count;
				bool flag = TryReadCount(__instance, currentLogger, out collectionResolved, out count);
				if (!flag)
				{
					LogWorldResultOnce(currentLogger, ref worldZeroLogged, collectionResolved, flag, count);
					return;
				}
				if (count == 0)
				{
					LogWorldResultOnce(currentLogger, ref worldZeroLogged, collectionResolved: true, countRead: true, count);
					return;
				}
				worldPositiveLogged = 1;
				LogWorldResult(currentLogger, collectionResolved: true, countRead: true, count);
				GeneralExactAllowlistFloatingPrototypeV2.ExecuteOnce(currentLogger);
			}
		}

		private static bool TryReadCount(object instance, ManualLogSource currentLogger, out bool collectionResolved, out int count)
		{
			collectionResolved = false;
			count = 0;
			if (instance == null || itemsField == null)
			{
				return false;
			}
			object value;
			try
			{
				value = itemsField.GetValue(instance);
			}
			catch (Exception exception)
			{
				LogError(currentLogger, "field read", exception);
				return false;
			}
			if (!(value is ICollection collection))
			{
				return false;
			}
			collectionResolved = true;
			try
			{
				count = collection.Count;
			}
			catch (Exception exception2)
			{
				LogError(currentLogger, "collection count", exception2);
				return false;
			}
			return true;
		}

		private static void LogWorldResultOnce(ManualLogSource currentLogger, ref int zeroLogged, bool collectionResolved, bool countRead, int count)
		{
			if (zeroLogged == 0)
			{
				zeroLogged = 1;
				LogWorldResult(currentLogger, collectionResolved, countRead, count);
			}
		}

		private static void LogWorldResult(ManualLogSource currentLogger, bool collectionResolved, bool countRead, int count)
		{
			LogInfo(currentLogger, "ObjectDB world item collection resolved: " + FormatBoolean(collectionResolved));
			LogInfo(currentLogger, "World-loaded potential item prefabs found: " + count);
			LogInfo(currentLogger, "ObjectDB ready after CopyOtherDB: " + FormatBoolean(countRead && count > 0));
			LogInfo(currentLogger, "Item collection enumerated: false");
			LogInfo(currentLogger, "Individual prefabs inspected: 0");
			LogInfo(currentLogger, "Gameplay objects modified: 0");
		}

		private static void LogInfo(ManualLogSource currentLogger, string message)
		{
			if (currentLogger != null)
			{
				currentLogger.LogInfo((object)message);
			}
		}

		private static void LogError(ManualLogSource currentLogger, string message)
		{
			if (currentLogger != null)
			{
				currentLogger.LogError((object)("ObjectDB item count probe error: " + message));
			}
		}

		private static void LogError(ManualLogSource currentLogger, string operation, Exception exception)
		{
			string text = exception.Message ?? string.Empty;
			if (text.Length > 160)
			{
				text = text.Substring(0, 160);
			}
			LogError(currentLogger, operation + " failed with " + exception.GetType().Name + ": " + text);
		}

		private static string FormatBoolean(bool value)
		{
			if (!value)
			{
				return "false";
			}
			return "true";
		}
	}
	internal sealed class GeneralExactAllowlistFloatingPrototypeV2
	{
		private struct PlanEntry
		{
			public readonly int CollectionIndex;

			public readonly int RootInstanceId;

			public readonly string PrefabName;

			public PlanEntry(int collectionIndex, int rootInstanceId, string prefabName)
			{
				CollectionIndex = collectionIndex;
				RootInstanceId = rootInstanceId;
				PrefabName = prefabName;
			}
		}

		private struct PlanningCounters
		{
			public int CollectionIterations;

			public int TotalObjectDbEntries;

			public int AllowlistNamesEncountered;

			public int EligibleCandidatesPlanned;

			public int DuplicateRootIdsDetected;

			public int DuplicatePrefabNamesDetected;

			public int PlanningExceptions;

			public int InvalidEntries;

			public int MissingItemDrop;

			public int MissingRigidbody;

			public int MissingHierarchyCollider;

			public int AlreadyHadFloating;

			public bool PlanningCompletedSuccessfully;
		}

		private struct MutationCounters
		{
			public int PlannedCandidates;

			public int RevalidationFailures;

			public int AlreadyHadFloatingAtRevalidation;

			public int AddComponentAttempts;

			public int AddComponentSuccesses;

			public int AddComponentExceptions;

			public int RootPrefabsModified;

			public int ComponentsAdded;

			public int InspectionErrors;
		}

		private const int ExactAllowlistCount = 482;

		private const int MaxCollectionCount = 5000;

		private const int MaxPlannedCandidates = 482;

		private static int s_executionCount;

		private static readonly HashSet<string> ExactIncludeNames = new HashSet<string>(StringComparer.Ordinal)
		{
			"AmberPearl", "ArmorAshlandsMediumChest", "ArmorAshlandsMediumlegs", "ArmorBerserkerChest", "ArmorBerserkerLegs", "ArmorBerserkerUndeadChest", "ArmorBerserkerUndeadLegs", "ArmorBronzeChest", "ArmorBronzeLegs", "ArmorCarapaceChest",
			"ArmorCarapaceLegs", "ArmorDress1", "ArmorDress10", "ArmorDress2", "ArmorDress3", "ArmorDress4", "ArmorDress5", "ArmorDress6", "ArmorDress7", "ArmorDress8",
			"ArmorDress9", "ArmorFenringChest", "ArmorFenringLegs", "ArmorFlametalChest", "ArmorFlametalLegs", "ArmorHarvester1", "ArmorHarvester2", "ArmorIronChest", "ArmorIronLegs", "ArmorLeatherChest",
			"ArmorLeatherLegs", "ArmorMageChest", "ArmorMageChest_Ashlands", "ArmorMageLegs", "ArmorMageLegs_Ashlands", "ArmorPaddedCuirass", "ArmorPaddedGreaves", "ArmorRagsChest", "ArmorRagsLegs", "ArmorRootChest",
			"ArmorRootLegs", "ArmorTrollLeatherChest", "ArmorTrollLeatherLegs", "ArmorTunic1", "ArmorTunic10", "ArmorTunic2", "ArmorTunic3", "ArmorTunic4", "ArmorTunic5", "ArmorTunic6",
			"ArmorTunic7", "ArmorTunic8", "ArmorTunic9", "ArmorWolfChest", "ArmorWolfLegs", "ArrowBronze", "ArrowCarapace", "ArrowCharred", "ArrowFire", "ArrowFlint",
			"ArrowFrost", "ArrowIron", "ArrowNeedle", "ArrowObsidian", "ArrowPoison", "ArrowSilver", "ArrowWood", "AskHide", "AsksvinMeat", "AtgeirBlackmetal",
			"AtgeirBronze", "AtgeirHimminAfl", "AtgeirIron", "AtgeirWood", "AxeBerzerkr", "AxeBerzerkrBlood", "AxeBerzerkrLightning", "AxeBerzerkrNature", "AxeBlackMetal", "AxeBronze",
			"AxeEarly", "AxeFlint", "AxeHead1", "AxeHead2", "AxeIron", "AxeJotunBane", "AxeStone", "AxeWood", "BarberKit", "BarleyFlour",
			"BarleyWine", "BarleyWineBase", "Battleaxe", "BattleaxeBlackmetal", "BattleaxeCrystal", "BattleaxeSkullSplittur", "BattleaxeWood", "BeechSeeds", "Bell", "BellFragment",
			"BeltStrength", "BirchSeeds", "BjornHide", "BjornMeat", "BjornPaw", "BlackCore", "BlackMarble", "BlackMetal", "BlackMetalScrap", "BlackSoup",
			"Bloodbag", "BloodPudding", "Blueberries", "BoarJerky", "BoltBlackmetal", "BoltBone", "BoltCarapace", "BoltCharred", "BoltIron", "BoneFragments",
			"BonemawSerpentScale", "Bread", "BreadDough", "Bronze", "BronzeNails", "BronzeScrap", "BugMeat", "CapeAsh", "CapeAsksvin", "CapeDeerHide",
			"CapeFeather", "CapeLinen", "CapeLox", "CapeOdin", "CapeTrollHide", "CapeWolf", "Carapace", "CarrotSeeds", "CarrotSoup", "Chain",
			"CharcoalResin", "CharredBone", "ChickenMeat", "Chitin", "Cloudberry", "Coal", "Coins", "CookedAsksvinMeat", "CookedBjornMeat", "CookedBugMeat",
			"CookedChickenMeat", "CookedDeerMeat", "CookedEgg", "CookedHareMeat", "CookedLoxMeat", "CookedMeat", "CookedVoltureMeat", "CookedWolfMeat", "Copper", "CopperOre",
			"CopperScrap", "Crystal", "Cultivator", "Dandelion", "DeerHide", "DeerMeat", "DeerStew", "Ectoplasm", "Eitr", "Entrails",
			"Eyescream", "FeastAshlands_Material", "FeastBlackforest_Material", "Feaster", "FeastMeadows_Material", "FeastMistlands_Material", "FeastMountains_Material", "FeastOceans_Material", "FeastPlains_Material", "FeastSwamps_Material",
			"Feathers", "Fiddleheadfern", "FierySvinstew", "Fish1", "Fish10", "Fish11", "Fish12", "Fish2", "Fish3", "Fish4_cave",
			"Fish5", "Fish6", "Fish7", "Fish8", "Fish9", "FishAndBread", "FishAndBreadUncooked", "FishAnglerRaw", "FishCooked", "FishRaw",
			"FishWraps", "FistBjornClaw", "FistBjornUndeadClaw", "FistFenrirClaw", "Flametal", "FlametalNew", "FlametalOre", "FlametalOreNew", "Flint", "GemstoneBlue",
			"GemstoneGreen", "GemstoneRed", "Grausten", "Guck", "Hammer", "HareMeat", "HelmetAshlandsMediumHood", "HelmetBerserkerHood", "HelmetBerserkerUndead", "HelmetBronze",
			"HelmetCarapace", "HelmetCelebration", "HelmetDrake", "HelmetDverger", "HelmetFenring", "HelmetFishingHat", "HelmetFlametal", "HelmetHat1", "HelmetHat10", "HelmetHat2",
			"HelmetHat3", "HelmetHat4", "HelmetHat5", "HelmetHat6", "HelmetHat7", "HelmetHat8", "HelmetHat9", "HelmetIron", "HelmetLeather", "HelmetMage",
			"HelmetMage_Ashlands", "HelmetMidsummerCrown", "HelmetOdin", "HelmetPadded", "HelmetPointyHat", "HelmetRoot", "HelmetStrawHat", "HelmetSweatBand", "HelmetTrollLeather", "HelmetYule",
			"HoneyGlazedChickenUncooked", "Iron", "IronNails", "IronOre", "Ironpit", "IronScrap", "KnifeBlackMetal", "KnifeChitin", "KnifeCopper", "KnifeFlint",
			"KnifeSilver", "KnifeWood", "LeatherScraps", "LoxMeat", "LoxPie", "LoxPieUncooked", "MaceSilver", "MagicallyStuffedShroomUncooked", "MashedMeat", "MeadBaseBugRepellent",
			"MeadBaseBzerker", "MeadBaseEitrLingering", "MeadBaseEitrMinor", "MeadBaseFrostResist", "MeadBaseHasty", "MeadBaseHealthLingering", "MeadBaseHealthMajor", "MeadBaseHealthMedium", "MeadBaseHealthMinor", "MeadBaseLightFoot",
			"MeadBasePoisonResist", "MeadBaseStaminaLingering", "MeadBaseStaminaMedium", "MeadBaseStaminaMinor", "MeadBaseStrength", "MeadBaseSwimmer", "MeadBaseTamer", "MeadBaseTasty", "MeadBugRepellent", "MeadBzerker",
			"MeadEitrLingering", "MeadEitrMinor", "MeadFrostResist", "MeadHasty", "MeadHealthLingering", "MeadHealthMajor", "MeadHealthMedium", "MeadHealthMinor", "MeadLightfoot", "MeadPoisonResist",
			"MeadStaminaLingering", "MeadStaminaMedium", "MeadStaminaMinor", "MeadStrength", "MeadTamer", "MeadTasty", "MeadTrollPheromones", "MeatPlatter", "MeatPlatterUncooked", "MinceMeatSauce",
			"MisthareSupremeUncooked", "MoltenCore", "MorgenSinew", "Mushroom", "MushroomBlue", "MushroomBzerker", "MushroomJotunPuffs", "MushroomMagecap", "MushroomOmelette", "MushroomSmokePuff",
			"MushroomYellow", "Obsidian", "OnionSeeds", "OnionSoup", "PickaxeAntler", "PickaxeBlackMetal", "PickaxeBronze", "PickaxeIron", "PickaxeStone", "PiquantPie",
			"PiquantPieUncooked", "Pukeberries", "Raspberry", "RawMeat", "Resin", "RoastedCrustPie", "RoastedCrustPieUncooked", "RottenMeat", "Salad", "Sausages",
			"ScaleHide", "Scythe", "ScytheHandle", "SeekerAspic", "SerpentMeatCooked", "SerpentScale", "SerpentStew", "SharpeningStone", "ShieldBronzeBuckler", "ShieldCore",
			"ShieldIronBuckler", "Silver", "SilverNecklace", "SilverOre", "SizzlingBerryBroth", "SledgeDemolisher", "SledgeIron", "SledgeWood", "Softtissue", "SpiceForests",
			"SpicyMarmalade", "StaffClusterbomb", "StaffFireball", "StaffGreenRoots", "StaffIceShards", "StaffRedTroll", "StaffShield", "StaffSkeleton", "Stone", "StoneRock",
			"SulfurStone", "SurtlingCore", "SwordBlackmetal", "SwordBronze", "SwordDyrnwyn", "SwordIron", "SwordIronFire", "SwordMistwalker", "SwordNiedhogg", "SwordNiedhoggBlood",
			"SwordNiedhoggLightning", "SwordNiedhoggNature", "SwordSilver", "SwordWood", "Tankard", "Tankard_dvergr", "TankardAnniversary", "TankardOdin", "Thistle", "THSwordKrom",
			"THSwordSlayer", "THSwordSlayerBlood", "THSwordSlayerLightning", "THSwordSlayerNature", "THSwordWood", "Thunderstone", "Tin", "TinOre", "Torch", "TrinketBlackDamageHealth",
			"TrinketBlackStamina", "TrinketBronzeHealth", "TrinketBronzeStamina", "TrinketCarapaceEitr", "TrinketChitinSwim", "TrinketFlametalEitr", "TrinketFlametalStaminaHealth", "TrinketIronHealth", "TrinketIronStamina", "TrinketScaleStaminaDamage",
			"TrinketSilverDamage", "TrinketSilverResist", "TrollHide", "TrophyAsksvin", "TrophyBjorn", "TrophyBjornUndead", "TrophyBlob", "TrophyBoar", "TrophyBonemawSerpent", "TrophyCharredArcher",
			"TrophyCharredMage", "TrophyCultist", "TrophyCultist_Hildir", "TrophyDeathsquito", "TrophyDeer", "TrophyDraugr", "TrophyDraugrElite", "TrophyDraugrFem", "TrophyDvergr", "TrophyFallenValkyrie",
			"TrophyFenring", "TrophyForestTroll", "TrophyFrostTroll", "TrophyGhost", "TrophyGjall", "TrophyGoblin", "TrophyGoblinBrute", "TrophyGoblinBruteBrosBrute", "TrophyGoblinBruteBrosShaman", "TrophyGoblinShaman",
			"TrophyGreydwarf", "TrophyGreydwarfBrute", "TrophyGreydwarfShaman", "TrophyGrowth", "TrophyHare", "TrophyHatchling", "TrophyKvastur", "TrophyLeech", "TrophyLox", "TrophyMorgen",
			"TrophyNeck", "TrophySeeker", "TrophySeekerBrute", "TrophySerpent", "TrophySGolem", "TrophySkeleton", "TrophySkeletonHildir", "TrophySkeletonPoison", "TrophySurtling", "TrophyTick",
			"TrophyUlv", "TrophyVolture", "TrophyWolf", "TrophyWraith", "TurnipSeeds", "TurnipStew", "TurretBoltBone", "TurretBoltFlametal", "TurretBoltWood", "VikingCupcakeUncooked",
			"Vineberry", "VineberrySeeds", "VineGreenSeeds", "Wisp", "WolfClaw", "WolfFang", "WolfHairBundle", "WolfJerky", "WolfMeat", "WolfMeatSkewer",
			"WolfPelt", "YggdrasilPorridge"
		};

		public static void ExecuteOnce(ManualLogSource logger)
		{
			if (Interlocked.Exchange(ref s_executionCount, 1) == 0)
			{
				if (!TryResolveRuntimeTypes(out var objectDbType, out var itemDropType, out var floatingType))
				{
					SafeLogError(logger, "General exact allowlist V2 stopped: runtime types unavailable.");
					return;
				}
				if (!TryResolveObjectDbInstance(objectDbType, out var objectDbInstance))
				{
					SafeLogError(logger, "General exact allowlist V2 stopped: ObjectDB instance unavailable.");
					return;
				}
				if (!TryBuildPlan(objectDbInstance, objectDbType, itemDropType, floatingType, out var items, out var itemCount, out var plan, out var counters))
				{
					SafeLogInfo(logger, "General exact allowlist V2 planning summary: Collection iterations: " + counters.CollectionIterations + "; Total ObjectDB entries: " + counters.TotalObjectDbEntries + "; Exact allowlist names: 482; Allowlist names encountered: " + counters.AllowlistNamesEncountered + "; Eligible candidates planned: " + counters.EligibleCandidatesPlanned + "; Duplicate root IDs detected: " + counters.DuplicateRootIdsDetected + "; Duplicate prefab names detected: " + counters.DuplicatePrefabNamesDetected + "; Planning exceptions: " + counters.PlanningExceptions + "; Planning completed successfully: false.");
					SafeLogError(logger, "General exact allowlist V2 stopped: planning failed before mutation.");
					return;
				}
				if (!counters.PlanningCompletedSuccessfully || plan == null || plan.Count > 482)
				{
					SafeLogError(logger, "General exact allowlist V2 stopped: planning commit boundary failed.");
					return;
				}
				SafeLogInfo(logger, "General exact allowlist V2 planning summary: Collection iterations: " + counters.CollectionIterations + "; Total ObjectDB entries: " + counters.TotalObjectDbEntries + "; Exact allowlist names: 482; Allowlist names encountered: " + counters.AllowlistNamesEncountered + "; Eligible candidates planned: " + counters.EligibleCandidatesPlanned + "; Duplicate root IDs detected: 0; Duplicate prefab names detected: 0; Planning exceptions: 0; Planning completed successfully: true.");
				SafeLogInfo(logger, "General exact allowlist V2 mutation phase entered.");
				MutationCounters counters2 = new MutationCounters
				{
					PlannedCandidates = plan.Count
				};
				ApplyMutationPlan(items, itemCount, itemDropType, floatingType, plan, logger, ref counters2);
				SafeLogInfo(logger, "General exact allowlist V2 mutation summary: General prototype executions: 1; Collection iterations: " + counters.CollectionIterations + "; Total ObjectDB entries: " + counters.TotalObjectDbEntries + "; Exact allowlist names: 482; Allowlist names encountered: " + counters.AllowlistNamesEncountered + "; Eligible candidates planned: " + counters.EligibleCandidatesPlanned + "; Planned candidates: " + counters2.PlannedCandidates + "; Revalidation failures: " + counters2.RevalidationFailures + "; Already had Floating at revalidation: " + counters2.AlreadyHadFloatingAtRevalidation + "; AddComponent attempts: " + counters2.AddComponentAttempts + "; AddComponent successes: " + counters2.AddComponentSuccesses + "; AddComponent exceptions: " + counters2.AddComponentExceptions + "; Root prefabs modified: " + counters2.RootPrefabsModified + "; Components added: " + counters2.ComponentsAdded + "; Inspection errors: " + counters2.InspectionErrors + "; Child objects modified: 0; Other prefabs modified: 0; Properties modified: 0; Unknown names accepted: 0; Manual-review names modified: 0; Excluded names modified: 0.");
			}
		}

		private static bool TryResolveRuntimeTypes(out Type objectDbType, out Type itemDropType, out Type floatingType)
		{
			objectDbType = null;
			itemDropType = null;
			floatingType = null;
			try
			{
				objectDbType = Type.GetType("ObjectDB, assembly_valheim", throwOnError: false);
				itemDropType = Type.GetType("ItemDrop, assembly_valheim", throwOnError: false);
				floatingType = Type.GetType("Floating, assembly_valheim", throwOnError: false);
				return objectDbType != null && itemDropType != null && floatingType != null;
			}
			catch (Exception)
			{
				objectDbType = null;
				itemDropType = null;
				floatingType = null;
				return false;
			}
		}

		private static bool TryResolveObjectDbInstance(Type objectDbType, out object objectDbInstance)
		{
			objectDbInstance = null;
			try
			{
				BindingFlags bindingAttr = BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;
				FieldInfo field = objectDbType.GetField("instance", bindingAttr);
				if (field == null)
				{
					field = objectDbType.GetField("m_instance", bindingAttr);
				}
				if (field == null)
				{
					return false;
				}
				objectDbInstance = field.GetValue(null);
				return objectDbInstance != null;
			}
			catch (Exception)
			{
				objectDbInstance = null;
				return false;
			}
		}

		private static bool TryBuildPlan(object objectDbInstance, Type objectDbType, Type itemDropType, Type floatingType, out IList items, out int itemCount, out List<PlanEntry> plan, out PlanningCounters counters)
		{
			items = null;
			itemCount = 0;
			plan = null;
			counters = default(PlanningCounters);
			try
			{
				if (objectDbInstance == null || objectDbType == null || itemDropType == null || floatingType == null || ExactIncludeNames.Count != 482)
				{
					return false;
				}
				BindingFlags bindingAttr = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
				FieldInfo field = objectDbType.GetField("m_items", bindingAttr);
				if (field == null)
				{
					return false;
				}
				items = field.GetValue(objectDbInstance) as IList;
				if (items == null)
				{
					return false;
				}
				itemCount = items.Count;
				counters.TotalObjectDbEntries = itemCount;
				if (itemCount < 1 || itemCount > 5000)
				{
					return false;
				}
				List<PlanEntry> list = new List<PlanEntry>(482);
				HashSet<int> hashSet = new HashSet<int>();
				HashSet<string> hashSet2 = new HashSet<string>(StringComparer.Ordinal);
				for (int i = 0; i < itemCount; i++)
				{
					counters.CollectionIterations++;
					object? obj = items[i];
					GameObject val = (GameObject)((obj is GameObject) ? obj : null);
					if ((Object)(object)val == (Object)null)
					{
						counters.InvalidEntries++;
						continue;
					}
					string name = ((Object)val).name;
					if (ExactIncludeNames.Contains(name))
					{
						counters.AllowlistNamesEncountered++;
						if (counters.AllowlistNamesEncountered > 482)
						{
							return false;
						}
						int instanceID = ((Object)val).GetInstanceID();
						if (!hashSet.Add(instanceID))
						{
							counters.DuplicateRootIdsDetected++;
							return false;
						}
						if (!hashSet2.Add(name))
						{
							counters.DuplicatePrefabNamesDetected++;
							return false;
						}
						if ((Object)(object)val.GetComponent(itemDropType) == (Object)null)
						{
							counters.MissingItemDrop++;
							continue;
						}
						if ((Object)(object)val.GetComponent<Rigidbody>() == (Object)null)
						{
							counters.MissingRigidbody++;
							continue;
						}
						if ((Object)(object)val.GetComponentInChildren(typeof(Collider), true) == (Object)null)
						{
							counters.MissingHierarchyCollider++;
							continue;
						}
						if ((Object)(object)val.GetComponent(floatingType) != (Object)null)
						{
							counters.AlreadyHadFloating++;
							continue;
						}
						list.Add(new PlanEntry(i, instanceID, name));
						counters.EligibleCandidatesPlanned++;
					}
				}
				if (list.Count > 482)
				{
					return false;
				}
				counters.PlanningCompletedSuccessfully = true;
				plan = list;
				return true;
			}
			catch (Exception)
			{
				counters.PlanningExceptions++;
				items = null;
				itemCount = 0;
				plan = null;
				counters.PlanningCompletedSuccessfully = false;
				return false;
			}
		}

		private static void ApplyMutationPlan(IList items, int itemCount, Type itemDropType, Type floatingType, List<PlanEntry> plan, ManualLogSource logger, ref MutationCounters counters)
		{
			HashSet<int> hashSet = new HashSet<int>();
			for (int i = 0; i < plan.Count; i++)
			{
				try
				{
					PlanEntry planEntry = plan[i];
					if (planEntry.CollectionIndex < 0 || planEntry.CollectionIndex >= itemCount)
					{
						counters.RevalidationFailures++;
						continue;
					}
					object? obj = items[planEntry.CollectionIndex];
					GameObject val = (GameObject)((obj is GameObject) ? obj : null);
					if ((Object)(object)val == (Object)null)
					{
						counters.RevalidationFailures++;
						continue;
					}
					int instanceID = ((Object)val).GetInstanceID();
					string name = ((Object)val).name;
					if (instanceID != planEntry.RootInstanceId || !string.Equals(name, planEntry.PrefabName, StringComparison.Ordinal) || !ExactIncludeNames.Contains(name))
					{
						counters.RevalidationFailures++;
						continue;
					}
					if ((Object)(object)val.GetComponent(itemDropType) == (Object)null || (Object)(object)val.GetComponent<Rigidbody>() == (Object)null || (Object)(object)val.GetComponentInChildren(typeof(Collider), true) == (Object)null)
					{
						counters.RevalidationFailures++;
						continue;
					}
					if ((Object)(object)val.GetComponent(floatingType) != (Object)null)
					{
						counters.AlreadyHadFloatingAtRevalidation++;
						continue;
					}
					if (!hashSet.Add(planEntry.RootInstanceId))
					{
						counters.RevalidationFailures++;
						continue;
					}
					try
					{
						Component obj2 = val.AddComponent(floatingType);
						counters.AddComponentAttempts++;
						if ((Object)(object)obj2 == (Object)null)
						{
							counters.InspectionErrors++;
							SafeLogError(logger, "General exact allowlist V2 stopped: AddComponent returned null.");
							break;
						}
						counters.AddComponentSuccesses++;
						counters.RootPrefabsModified++;
						counters.ComponentsAdded++;
					}
					catch (Exception)
					{
						counters.AddComponentAttempts++;
						counters.AddComponentExceptions++;
						counters.InspectionErrors++;
						SafeLogError(logger, "General exact allowlist V2 stopped: AddComponent threw.");
						break;
					}
				}
				catch (Exception)
				{
					counters.RevalidationFailures++;
					counters.InspectionErrors++;
					SafeLogWarning(logger, "General exact allowlist V2 skipped a candidate after revalidation failure.");
				}
			}
		}

		private static void SafeLogInfo(ManualLogSource logger, string message)
		{
			try
			{
				if (logger != null)
				{
					logger.LogInfo((object)message);
				}
			}
			catch (Exception)
			{
			}
		}

		private static void SafeLogWarning(ManualLogSource logger, string message)
		{
			try
			{
				if (logger != null)
				{
					logger.LogWarning((object)message);
				}
			}
			catch (Exception)
			{
			}
		}

		private static void SafeLogError(ManualLogSource logger, string message)
		{
			try
			{
				if (logger != null)
				{
					logger.LogError((object)message);
				}
			}
			catch (Exception)
			{
			}
		}
	}
}