Decompiled source of CheatCleanup v1.0.0

plugins/CheatCleanup/CheatCleanup.dll

Decompiled a month 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.Text;
using BepInEx;
using BepInEx.Logging;
using Jotunn.Entities;
using Jotunn.Managers;
using Microsoft.CodeAnalysis;
using UnityEngine;

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

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace CheatCleanup
{
	internal sealed class CheatCleanupCommand : ConsoleCommand
	{
		public override string Name => "cheatcleanup";

		public override string Help => "Limpa permanentemente flags de cheat do personagem/mundo. Use: cheatcleanup scan | clean | clean confirm | verify";

		public override bool IsCheat => false;

		public override bool IsNetwork => false;

		public override bool IsSecret => false;

		public override List<string> CommandOptionList()
		{
			return new List<string> { "scan", "clean", "verify", "help" };
		}

		public override void Run(string[] args, Terminal context)
		{
			string text = ((args != null && args.Length != 0) ? args[0].ToLowerInvariant() : "help");
			switch (text)
			{
			case "help":
			case "status":
				Print(context, "CheatCleanup v1.0.0\nNada e alterado automaticamente.\ncheatcleanup scan          - varredura sem alterar o save\ncheatcleanup clean         - mostra a confirmacao\ncheatcleanup clean confirm - executa a limpeza e salva\ncheatcleanup verify        - nova varredura para conferir\nSeguranca: limpeza bloqueada em servidor de terceiros.");
				return;
			case "clean":
				if (args.Length < 2 || !string.Equals(args[1], "confirm", StringComparison.OrdinalIgnoreCase))
				{
					Print(context, "ATENCAO: a limpeza modifica permanentemente o personagem e o mundo atualmente carregados.\nFaca um backup do mundo/personagem antes do primeiro teste.\nPrimeiro recomendo: cheatcleanup scan\nPara executar: cheatcleanup clean confirm");
					return;
				}
				break;
			}
			if (text != "scan" && text != "verify" && text != "clean")
			{
				Print(context, "Subcomando desconhecido. Use: cheatcleanup help");
				return;
			}
			if (!CleanupService.ValidateEnvironment(out var reason))
			{
				Print(context, "[CheatCleanup] " + reason);
				return;
			}
			bool flag = text == "clean";
			Print(context, "[CheatCleanup] " + (flag ? "Limpando" : "Analisando") + " mundo '" + ValheimAccess.GetWorldName() + "'...");
			CleanupReport cleanupReport = CleanupService.Run(flag);
			string heading = ((text == "verify") ? "VERIFICACAO" : (flag ? "LIMPEZA" : "SCAN"));
			Print(context, cleanupReport.ToConsoleText(heading));
			if (text == "verify")
			{
				Print(context, cleanupReport.PersistentDataLooksClean ? "[CheatCleanup] DADOS PERSISTENTES LIMPOS. Game.isModded ainda sera true nesta sessao; feche e abra o Valheim sem mods para a verificacao vanilla." : "[CheatCleanup] Ainda existem marcadores persistentes de cheat ou itens que nao puderam ser confirmados como limpos.");
			}
		}

		private static void Print(Terminal context, string text)
		{
			if ((Object)(object)context != (Object)null)
			{
				context.AddString(text);
			}
			ManualLogSource modLog = CheatCleanupPlugin.ModLog;
			if (modLog != null)
			{
				modLog.LogInfo((object)text);
			}
		}
	}
	[BepInPlugin("br.cheatcleanup.valheim", "CheatCleanup", "1.0.0")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public sealed class CheatCleanupPlugin : BaseUnityPlugin
	{
		public const string PluginGuid = "br.cheatcleanup.valheim";

		public const string PluginName = "CheatCleanup";

		public const string PluginVersion = "1.0.0";

		internal static ManualLogSource ModLog { get; private set; }

		private void Awake()
		{
			ModLog = ((BaseUnityPlugin)this).Logger;
			CommandManager.Instance.AddConsoleCommand((ConsoleCommand)(object)new CheatCleanupCommand());
			((BaseUnityPlugin)this).Logger.LogInfo((object)"CheatCleanup 1.0.0 carregado. Nenhuma limpeza e executada automaticamente.");
		}
	}
	internal sealed class CleanupReport
	{
		public bool ApplyMode;

		public bool CharacterFlagFound;

		public bool CharacterFlagChanged;

		public bool WorldWasCheated;

		public bool WorldStillCheated;

		public bool SaveRequested;

		public int PlayerItemsScanned;

		public int PlayerItemsCheated;

		public int PlayerItemsChanged;

		public int ZdosScanned;

		public int ZdoCheatedFlags;

		public int ZdoCheatedQueuedFlags;

		public int ZdoFlagsChanged;

		public int InventoryPayloadsScanned;

		public int InventoryItemsScanned;

		public int InventoryItemsCheated;

		public int InventoryPayloadsChanged;

		public int ItemPayloadsScanned;

		public int ItemPayloadsCheated;

		public int ItemPayloadsChanged;

		public int RuntimeContainersScanned;

		public int RuntimeDropsScanned;

		public int RuntimeObjectsChanged;

		public int WorldKeysScanned;

		public readonly List<string> InvalidWorldKeys = new List<string>();

		public readonly List<string> CheatedItemDetails = new List<string>();

		public int WorldKeysChanged;

		public readonly List<string> Warnings = new List<string>();

		public readonly List<string> Errors = new List<string>();

		public int TotalCheatMarkersFound => (CharacterFlagFound ? 1 : 0) + PlayerItemsCheated + ZdoCheatedFlags + ZdoCheatedQueuedFlags + InventoryItemsCheated + ItemPayloadsCheated + InvalidWorldKeys.Count;

		public bool PersistentDataLooksClean
		{
			get
			{
				if (!CharacterFlagFound && PlayerItemsCheated == 0 && ZdoCheatedFlags == 0 && ZdoCheatedQueuedFlags == 0 && InventoryItemsCheated == 0 && ItemPayloadsCheated == 0 && InvalidWorldKeys.Count == 0)
				{
					return !WorldWasCheated;
				}
				return false;
			}
		}

		public string ToConsoleText(string heading)
		{
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.AppendLine("=== CheatCleanup: " + heading + " ===");
			stringBuilder.AppendLine("Personagem marcado: " + (CharacterFlagFound ? "SIM" : "NAO") + ((!ApplyMode || !CharacterFlagFound) ? string.Empty : (CharacterFlagChanged ? " -> LIMPO" : " -> NAO ALTERADO")));
			stringBuilder.AppendLine($"Inventario do personagem: {PlayerItemsScanned} itens, {PlayerItemsCheated} marcados" + (ApplyMode ? $", {PlayerItemsChanged} limpos" : string.Empty));
			stringBuilder.AppendLine($"ZDOs do mundo: {ZdosScanned} verificados; flags cheated={ZdoCheatedFlags}, queued={ZdoCheatedQueuedFlags}" + (ApplyMode ? $", {ZdoFlagsChanged} removidas" : string.Empty));
			stringBuilder.AppendLine($"Inventarios persistentes (baus/tumulos/navios/etc.): {InventoryPayloadsScanned} payloads, {InventoryItemsScanned} itens, {InventoryItemsCheated} marcados" + (ApplyMode ? $", {InventoryPayloadsChanged} payloads regravados" : string.Empty));
			stringBuilder.AppendLine($"Itens persistentes do mapa/stands: {ItemPayloadsScanned} payloads, {ItemPayloadsCheated} marcados" + (ApplyMode ? $", {ItemPayloadsChanged} regravados" : string.Empty));
			stringBuilder.AppendLine($"World keys: {WorldKeysScanned} verificadas, {InvalidWorldKeys.Count} consideradas cheat pelo criterio do proprio Valheim" + (ApplyMode ? $", {WorldKeysChanged} removidas" : string.Empty));
			stringBuilder.AppendLine("WorldContainsCheatedModifiers: antes=" + (WorldWasCheated ? "SIM" : "NAO") + (ApplyMode ? (", depois=" + (WorldStillCheated ? "SIM" : "NAO")) : string.Empty));
			if (CheatedItemDetails.Count > 0)
			{
				stringBuilder.AppendLine("Detalhes dos itens marcados:");
				foreach (string cheatedItemDetail in CheatedItemDetails)
				{
					stringBuilder.AppendLine(" - " + cheatedItemDetail);
				}
			}
			if (InvalidWorldKeys.Count > 0)
			{
				stringBuilder.AppendLine("Keys suspeitas: " + string.Join(", ", InvalidWorldKeys));
			}
			if (Warnings.Count > 0)
			{
				stringBuilder.AppendLine("Avisos:");
				foreach (string warning in Warnings)
				{
					stringBuilder.AppendLine(" - " + warning);
				}
			}
			if (Errors.Count > 0)
			{
				stringBuilder.AppendLine("Erros:");
				foreach (string error in Errors)
				{
					stringBuilder.AppendLine(" - " + error);
				}
			}
			if (ApplyMode)
			{
				stringBuilder.AppendLine(SaveRequested ? "Salvamento do mundo e perfis solicitado ao Valheim." : "ATENCAO: nao foi possivel solicitar o salvamento automaticamente.");
			}
			return stringBuilder.ToString().TrimEnd(Array.Empty<char>());
		}
	}
	internal static class CleanupService
	{
		private const int IndexedItemDataSlotsToCheck = 32;

		internal static bool ValidateEnvironment(out string reason)
		{
			if (!ValheimAccess.IsReady(out reason))
			{
				return false;
			}
			if (!ValheimAccess.IsServer())
			{
				reason = "Seguranca: CheatCleanup so pode alterar single-player ou um mundo que voce esteja hospedando. Em servidor de terceiros o comando e bloqueado.";
				return false;
			}
			return true;
		}

		internal static CleanupReport Run(bool apply)
		{
			CleanupReport cleanupReport = new CleanupReport
			{
				ApplyMode = apply
			};
			try
			{
				ScanCharacter(cleanupReport, apply);
			}
			catch (Exception ex)
			{
				cleanupReport.Errors.Add("Personagem: " + RootMessage(ex));
			}
			try
			{
				ScanPlayerInventory(cleanupReport, apply);
			}
			catch (Exception ex2)
			{
				cleanupReport.Errors.Add("Inventario do personagem: " + RootMessage(ex2));
			}
			try
			{
				ScanWorldZdos(cleanupReport, apply);
			}
			catch (Exception ex3)
			{
				cleanupReport.Errors.Add("ZDOs persistentes: " + RootMessage(ex3));
			}
			try
			{
				SynchronizeLoadedRuntimeObjects(cleanupReport, apply);
			}
			catch (Exception ex4)
			{
				cleanupReport.Errors.Add("Objetos carregados: " + RootMessage(ex4));
			}
			try
			{
				ScanWorldKeys(cleanupReport, apply);
			}
			catch (Exception ex5)
			{
				cleanupReport.Errors.Add("World keys: " + RootMessage(ex5));
			}
			if (apply && cleanupReport.Errors.Count == 0)
			{
				try
				{
					cleanupReport.SaveRequested = ValheimAccess.RequestNativeSave();
				}
				catch (Exception ex6)
				{
					cleanupReport.Errors.Add("Salvamento: " + RootMessage(ex6));
				}
			}
			return cleanupReport;
		}

		private static void ScanCharacter(CleanupReport report, bool apply)
		{
			object profile = ValheimAccess.GetProfile();
			if (profile == null)
			{
				report.Warnings.Add("PlayerProfile nao encontrado.");
			}
			else if (apply & (report.CharacterFlagFound = ValheimAccess.ProfileUsedCheats(profile)))
			{
				report.CharacterFlagChanged = ValheimAccess.SetProfileUsedCheats(profile, value: false);
				ValheimAccess.ResetCheatStat(profile);
			}
		}

		private static void ScanPlayerInventory(CleanupReport report, bool apply)
		{
			foreach (object item in ValheimAccess.InventoryItems(ValheimAccess.GetLocalPlayerInventory()))
			{
				report.PlayerItemsScanned++;
				if (ValheimAccess.ItemIsCheated(item))
				{
					report.PlayerItemsCheated++;
					AddCheatedItemDetail(report, "Inventario do personagem", null, item);
					if (apply && ValheimAccess.SetItemCheated(item, value: false))
					{
						report.PlayerItemsChanged++;
					}
				}
			}
		}

		private static void ScanWorldZdos(CleanupReport report, bool apply)
		{
			List<object> allZdos = ValheimAccess.GetAllZdos();
			if (allZdos.Count == 0)
			{
				report.Warnings.Add("Nenhum ZDO foi enumerado. A varredura do mapa nao pode ser considerada completa.");
				return;
			}
			int cheatedHash = ValheimAccess.CheatedHash;
			int cheatedQueuedHash = ValheimAccess.CheatedQueuedHash;
			int itemsHash = ValheimAccess.ItemsHash;
			int itemDataHash = ValheimAccess.ItemDataHash;
			int[] array = (from i in Enumerable.Range(0, 32)
				select ValheimAccess.StableHash(i + "_itemData")).ToArray();
			for (int num = 0; num < allZdos.Count; num++)
			{
				object zdo = allZdos[num];
				report.ZdosScanned++;
				if (ValheimAccess.GetZdoBool(zdo, cheatedHash))
				{
					report.ZdoCheatedFlags++;
					if (apply)
					{
						ValheimAccess.RemoveZdoBool(zdo, cheatedHash);
						report.ZdoFlagsChanged++;
					}
				}
				if (ValheimAccess.GetZdoBool(zdo, cheatedQueuedHash))
				{
					report.ZdoCheatedQueuedFlags++;
					if (apply)
					{
						ValheimAccess.RemoveZdoBool(zdo, cheatedQueuedHash);
						report.ZdoFlagsChanged++;
					}
				}
				byte[] zdoBytes = ValheimAccess.GetZdoBytes(zdo, itemsHash);
				if (zdoBytes != null && zdoBytes.Length != 0)
				{
					ScanInventoryPayload(zdo, itemsHash, zdoBytes, report, apply);
				}
				byte[] zdoBytes2 = ValheimAccess.GetZdoBytes(zdo, itemDataHash);
				if (zdoBytes2 != null && zdoBytes2.Length != 0)
				{
					ScanItemPayload(zdo, itemDataHash, zdoBytes2, report, apply);
				}
				foreach (int hash in array)
				{
					byte[] zdoBytes3 = ValheimAccess.GetZdoBytes(zdo, hash);
					if (zdoBytes3 != null && zdoBytes3.Length != 0)
					{
						ScanItemPayload(zdo, hash, zdoBytes3, report, apply);
					}
				}
			}
		}

		private static void ScanInventoryPayload(object zdo, int hash, byte[] bytes, CleanupReport report, bool apply)
		{
			report.InventoryPayloadsScanned++;
			object inventory = ValheimAccess.NewTemporaryInventory();
			object package = ValheimAccess.NewPackage(bytes);
			ValheimAccess.InventoryLoad(inventory, package);
			bool flag = false;
			foreach (object item in ValheimAccess.InventoryItems(inventory))
			{
				report.InventoryItemsScanned++;
				if (ValheimAccess.ItemIsCheated(item))
				{
					report.InventoryItemsCheated++;
					AddCheatedItemDetail(report, "Inventario persistente", zdo, item);
					if (apply && ValheimAccess.SetItemCheated(item, value: false))
					{
						flag = true;
					}
				}
			}
			if (apply && flag)
			{
				object package2 = ValheimAccess.NewPackage();
				ValheimAccess.InventorySave(inventory, package2);
				ValheimAccess.SetZdoBytes(zdo, hash, ValheimAccess.PackageGetArray(package2));
				report.InventoryPayloadsChanged++;
			}
		}

		private static void ScanItemPayload(object zdo, int hash, byte[] bytes, CleanupReport report, bool apply)
		{
			report.ItemPayloadsScanned++;
			object obj;
			try
			{
				obj = ValheimAccess.LoadDirectItemData(bytes, out var _);
			}
			catch
			{
				return;
			}
			if (obj != null && ValheimAccess.ItemIsCheated(obj))
			{
				report.ItemPayloadsCheated++;
				AddCheatedItemDetail(report, "Item persistente/stand", zdo, obj);
				if (apply && ValheimAccess.SetItemCheated(obj, value: false))
				{
					byte[] bytes2 = ValheimAccess.SaveDirectItemData(obj);
					ValheimAccess.SetZdoBytes(zdo, hash, bytes2);
					report.ItemPayloadsChanged++;
				}
			}
		}

		private static void SynchronizeLoadedRuntimeObjects(CleanupReport report, bool apply)
		{
			foreach (object item in ValheimAccess.FindLoadedObjects("Container"))
			{
				object obj = ReflectionUtil.Method(item.GetType(), "GetInventory", 0, false)?.Invoke(item, null) ?? ReflectionUtil.GetField(item, "m_inventory");
				if (obj != null)
				{
					report.RuntimeContainersScanned++;
					bool flag = CleanRuntimeInventory(obj, apply);
					if (apply && flag)
					{
						report.RuntimeObjectsChanged++;
						TryInvokeNoArg(item, "Save");
					}
				}
			}
			foreach (object item2 in ValheimAccess.FindLoadedObjects("ItemDrop"))
			{
				object field = ReflectionUtil.GetField(item2, "m_itemData");
				if (field != null)
				{
					report.RuntimeDropsScanned++;
					if (ValheimAccess.ItemIsCheated(field) && apply && ValheimAccess.SetItemCheated(field, value: false))
					{
						report.RuntimeObjectsChanged++;
						TryInvokeNoArg(item2, "Save");
					}
				}
			}
			bool flag2 = default(bool);
			foreach (object item3 in ValheimAccess.FindLoadedObjects("Fermenter"))
			{
				if (apply)
				{
					object field2 = ReflectionUtil.GetField(item3, "m_delayedTapItemCheated");
					int num;
					if (field2 is bool)
					{
						flag2 = (bool)field2;
						num = 1;
					}
					else
					{
						num = 0;
					}
					if (((uint)num & (flag2 ? 1u : 0u)) != 0 && ReflectionUtil.SetField(item3, "m_delayedTapItemCheated", false))
					{
						report.RuntimeObjectsChanged++;
					}
				}
			}
			bool flag3 = default(bool);
			foreach (object item4 in ValheimAccess.FindLoadedObjects("CharacterDrop"))
			{
				if (apply)
				{
					object field3 = ReflectionUtil.GetField(item4, "m_cheated");
					int num2;
					if (field3 is bool)
					{
						flag3 = (bool)field3;
						num2 = 1;
					}
					else
					{
						num2 = 0;
					}
					if (((uint)num2 & (flag3 ? 1u : 0u)) != 0 && ReflectionUtil.SetField(item4, "m_cheated", false))
					{
						report.RuntimeObjectsChanged++;
					}
				}
			}
		}

		private static bool CleanRuntimeInventory(object inventory, bool apply)
		{
			bool result = false;
			foreach (object item in ValheimAccess.InventoryItems(inventory))
			{
				if (ValheimAccess.ItemIsCheated(item))
				{
					result = true;
					if (apply)
					{
						ValheimAccess.SetItemCheated(item, value: false);
					}
				}
			}
			return result;
		}

		private static void ScanWorldKeys(CleanupReport report, bool apply)
		{
			object world = ValheimAccess.GetWorld();
			if (world == null)
			{
				return;
			}
			report.WorldWasCheated = ValheimAccess.WorldContainsCheatedModifiers(world);
			object startingWorldKeys = ValheimAccess.GetStartingWorldKeys(world);
			IReadOnlyCollection<string> allowedWorldKeys = ValheimAccess.GetAllowedWorldKeys();
			if (startingWorldKeys == null)
			{
				report.Warnings.Add("m_startingGlobalKeys nao encontrado; world modifiers nao foram alterados.");
				return;
			}
			if (allowedWorldKeys == null)
			{
				report.Warnings.Add("GetPossibleGlobalKeyValues nao encontrado; world modifiers nao foram alterados para evitar falsos positivos.");
				return;
			}
			HashSet<string> hashSet = new HashSet<string>(allowedWorldKeys, StringComparer.Ordinal);
			List<object> list = new List<object>();
			foreach (object item in ReflectionUtil.Enumerate(startingWorldKeys))
			{
				string text = Convert.ToString(item);
				report.WorldKeysScanned++;
				if (!string.IsNullOrEmpty(text) && !text.StartsWith("preset", StringComparison.Ordinal) && !hashSet.Contains(text))
				{
					report.InvalidWorldKeys.Add(text);
					list.Add(item);
				}
			}
			if (!apply)
			{
				return;
			}
			foreach (object item2 in list)
			{
				if (ValheimAccess.RemoveWorldKey(startingWorldKeys, item2))
				{
					report.WorldKeysChanged++;
				}
			}
			report.WorldStillCheated = ValheimAccess.WorldContainsCheatedModifiers(world);
		}

		private static void AddCheatedItemDetail(CleanupReport report, string source, object zdo, object item)
		{
			if (report.CheatedItemDetails.Count < 100)
			{
				string itemDisplayName = GetItemDisplayName(item);
				string itemStack = GetItemStack(item);
				string text = ((zdo == null) ? string.Empty : (" | " + DescribeZdo(zdo)));
				report.CheatedItemDetails.Add(source + ": " + itemDisplayName + itemStack + text);
			}
		}

		private static string GetItemDisplayName(object item)
		{
			if (item == null)
			{
				return "<item desconhecido>";
			}
			try
			{
				object field = ReflectionUtil.GetField(item, "m_shared");
				if (field != null)
				{
					string text = Convert.ToString(ReflectionUtil.GetField(field, "m_name"));
					if (!string.IsNullOrWhiteSpace(text))
					{
						return text;
					}
				}
				object field2 = ReflectionUtil.GetField(item, "m_dropPrefab");
				if (field2 != null)
				{
					string text2 = Convert.ToString(ReflectionUtil.GetProperty(field2, "name") ?? ReflectionUtil.GetField(field2, "name"));
					if (!string.IsNullOrWhiteSpace(text2))
					{
						return text2;
					}
				}
			}
			catch
			{
			}
			return "<item sem nome>";
		}

		private static string GetItemStack(object item)
		{
			try
			{
				object field = ReflectionUtil.GetField(item, "m_stack");
				if (field != null)
				{
					return " x" + Convert.ToInt32(field);
				}
			}
			catch
			{
			}
			return string.Empty;
		}

		private static string DescribeZdo(object zdo)
		{
			if (zdo == null)
			{
				return "ZDO desconhecido";
			}
			string text = "?";
			string text2 = "?";
			try
			{
				object obj = ReflectionUtil.Method(zdo.GetType(), "GetPosition", 0, false)?.Invoke(zdo, null);
				if (obj != null)
				{
					text = Convert.ToString(obj);
				}
			}
			catch
			{
			}
			try
			{
				object obj3 = ReflectionUtil.Method(zdo.GetType(), "GetPrefab", 0, false)?.Invoke(zdo, null);
				if (obj3 != null)
				{
					text2 = Convert.ToString(obj3);
				}
			}
			catch
			{
			}
			return "ZDO prefab=" + text2 + ", pos=" + text;
		}

		private static void TryInvokeNoArg(object instance, string methodName)
		{
			try
			{
				ReflectionUtil.Method(instance?.GetType(), methodName, 0, false)?.Invoke(instance, null);
			}
			catch
			{
			}
		}

		private static string RootMessage(Exception ex)
		{
			while (ex is TargetInvocationException { InnerException: not null } ex2)
			{
				ex = ex2.InnerException;
			}
			return ex.GetType().Name + ": " + ex.Message;
		}
	}
	internal static class ReflectionUtil
	{
		internal const BindingFlags AnyInstance = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;

		internal const BindingFlags AnyStatic = BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;

		internal const BindingFlags Any = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;

		internal static Type FindType(string simpleOrFullName)
		{
			Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
			foreach (Assembly assembly in assemblies)
			{
				Type type = assembly.GetType(simpleOrFullName, throwOnError: false);
				if (type != null)
				{
					return type;
				}
				try
				{
					Type type2 = assembly.GetTypes().FirstOrDefault((Type t) => string.Equals(t.Name, simpleOrFullName, StringComparison.Ordinal) || string.Equals(t.FullName, simpleOrFullName, StringComparison.Ordinal));
					if (type2 != null)
					{
						return type2;
					}
				}
				catch (ReflectionTypeLoadException ex)
				{
					Type type3 = ex.Types?.FirstOrDefault((Type t) => t != null && (string.Equals(t.Name, simpleOrFullName, StringComparison.Ordinal) || string.Equals(t.FullName, simpleOrFullName, StringComparison.Ordinal)));
					if (type3 != null)
					{
						return type3;
					}
				}
			}
			return null;
		}

		internal static FieldInfo Field(Type type, string name)
		{
			return type?.GetField(name, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
		}

		internal static PropertyInfo Property(Type type, string name)
		{
			return type?.GetProperty(name, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
		}

		internal static object GetStaticField(Type type, string name)
		{
			return Field(type, name)?.GetValue(null);
		}

		internal static object GetField(object instance, string name)
		{
			if (instance != null)
			{
				return Field(instance.GetType(), name)?.GetValue(instance);
			}
			return null;
		}

		internal static object GetProperty(object instance, string name)
		{
			if (instance == null)
			{
				return null;
			}
			return Property(instance.GetType(), name)?.GetValue(instance, null);
		}

		internal static bool SetField(object instance, string name, object value)
		{
			if (instance == null)
			{
				return false;
			}
			FieldInfo fieldInfo = Field(instance.GetType(), name);
			if (fieldInfo == null)
			{
				return false;
			}
			fieldInfo.SetValue(instance, value);
			return true;
		}

		internal static int GetStaticInt(Type type, string name)
		{
			object staticField = GetStaticField(type, name);
			if (staticField is int)
			{
				return (int)staticField;
			}
			return 0;
		}

		internal static MethodInfo Method(Type type, string name, int parameterCount = -1, bool? isStatic = null)
		{
			if (type == null)
			{
				return null;
			}
			return type.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault((MethodInfo m) => m.Name == name && (parameterCount < 0 || m.GetParameters().Length == parameterCount) && (!isStatic.HasValue || m.IsStatic == isStatic.Value));
		}

		internal static MethodInfo Method(Type type, string name, params Type[] parameterTypes)
		{
			if (type == null)
			{
				return null;
			}
			return type.GetMethod(name, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, parameterTypes, null);
		}

		internal static IEnumerable<object> Enumerate(object sequence)
		{
			if (!(sequence is IEnumerable enumerable))
			{
				yield break;
			}
			foreach (object item in enumerable)
			{
				yield return item;
			}
		}

		internal static object DictionaryValue(object entry)
		{
			if (entry == null)
			{
				return null;
			}
			if (entry is DictionaryEntry dictionaryEntry)
			{
				return dictionaryEntry.Value;
			}
			return entry.GetType().GetProperty("Value", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(entry, null);
		}
	}
	internal static class ValheimAccess
	{
		private static readonly Type GameType = ReflectionUtil.FindType("Game");

		private static readonly Type PlayerType = ReflectionUtil.FindType("Player");

		private static readonly Type ZNetType = ReflectionUtil.FindType("ZNet");

		private static readonly Type ZdoManType = ReflectionUtil.FindType("ZDOMan");

		private static readonly Type ZdoType = ReflectionUtil.FindType("ZDO");

		private static readonly Type ZdoVarsType = ReflectionUtil.FindType("ZDOVars");

		private static readonly Type ZPackageType = ReflectionUtil.FindType("ZPackage");

		private static readonly Type InventoryType = ReflectionUtil.FindType("Inventory");

		private static readonly Type ItemDataType = FindItemDataType();

		private static readonly Type WorldOptionsType = ReflectionUtil.FindType("ServerOptionsGUI");

		private static readonly Type PlayerStatType = ReflectionUtil.FindType("PlayerStatType");

		private static readonly MethodInfo ZdoGetBoolMethod = FindZdoMethod("GetBool", typeof(int), typeof(bool));

		private static readonly MethodInfo ZdoGetBytesMethod = FindZdoMethod("GetByteArray", typeof(int), typeof(byte[]));

		private static readonly MethodInfo ZdoSetBytesMethod = FindZdoMethod("Set", typeof(int), typeof(byte[]));

		private static readonly MethodInfo ZdoRemoveBoolMethod = FindZdoMethod("RemoveBool", typeof(int));

		private static MethodInfo _stableHashMethod;

		internal static object GameInstance => GetStaticMember(GameType, "instance") ?? GetStaticMember(GameType, "m_instance") ?? GetStaticMember(GameType, "Instance");

		internal static object ZNetInstance => GetStaticMember(ZNetType, "instance") ?? GetStaticMember(ZNetType, "m_instance") ?? GetStaticMember(ZNetType, "Instance");

		internal static object ZdoManInstance => GetStaticMember(ZdoManType, "instance") ?? GetStaticMember(ZdoManType, "m_instance") ?? GetStaticMember(ZdoManType, "Instance");

		internal static object LocalPlayer => GetStaticMember(PlayerType, "m_localPlayer") ?? GetStaticMember(PlayerType, "localPlayer") ?? GetStaticMember(PlayerType, "LocalPlayer");

		internal static int CheatedHash => ReflectionUtil.GetStaticInt(ZdoVarsType, "s_cheated");

		internal static int CheatedQueuedHash => ReflectionUtil.GetStaticInt(ZdoVarsType, "s_cheatedQueued");

		internal static int ItemsHash => ReflectionUtil.GetStaticInt(ZdoVarsType, "s_items");

		internal static int ItemDataHash => ReflectionUtil.GetStaticInt(ZdoVarsType, "s_itemData");

		internal static bool IsReady(out string reason)
		{
			if (ZNetInstance == null)
			{
				reason = "ZNet.instance ainda nao esta disponivel.";
				return false;
			}
			if (ZdoManInstance == null)
			{
				reason = "ZDOMan.instance ainda nao esta disponivel.";
				return false;
			}
			if (LocalPlayer == null)
			{
				reason = "Nenhum personagem local esta carregado.";
				return false;
			}
			if (GetWorld() == null)
			{
				reason = "Nenhum mundo esta carregado.";
				return false;
			}
			if (GetProfile() == null)
			{
				reason = "PlayerProfile do personagem atual nao foi encontrado.";
				return false;
			}
			reason = null;
			return true;
		}

		internal static bool IsServer()
		{
			object zNetInstance = ZNetInstance;
			MethodInfo methodInfo = ReflectionUtil.Method(ZNetType, "IsServer", 0, false);
			bool flag = default(bool);
			int num;
			if (zNetInstance != null && methodInfo != null)
			{
				object obj = methodInfo.Invoke(zNetInstance, null);
				if (obj is bool)
				{
					flag = (bool)obj;
					num = 1;
				}
				else
				{
					num = 0;
				}
			}
			else
			{
				num = 0;
			}
			return (byte)((uint)num & (flag ? 1u : 0u)) != 0;
		}

		internal static object GetWorld()
		{
			object zNetInstance = ZNetInstance;
			MethodInfo methodInfo = ReflectionUtil.Method(ZNetType, "GetWorld", 0, false);
			if (zNetInstance != null && !(methodInfo == null))
			{
				return methodInfo.Invoke(zNetInstance, null);
			}
			return null;
		}

		internal static string GetWorldName()
		{
			object zNetInstance = ZNetInstance;
			MethodInfo methodInfo = ReflectionUtil.Method(ZNetType, "GetWorldName", 0, false);
			if (zNetInstance != null && !(methodInfo == null))
			{
				return Convert.ToString(methodInfo.Invoke(zNetInstance, null));
			}
			return "?";
		}

		internal static object GetProfile()
		{
			object gameInstance = GameInstance;
			if (gameInstance != null)
			{
				MethodInfo methodInfo = ReflectionUtil.Method(gameInstance.GetType(), "GetPlayerProfile", 0, false);
				if (methodInfo != null)
				{
					object obj = methodInfo.Invoke(gameInstance, null);
					if (obj != null)
					{
						return obj;
					}
				}
				object obj2 = ReflectionUtil.GetField(gameInstance, "m_playerProfile") ?? ReflectionUtil.GetField(gameInstance, "m_profile");
				if (obj2 != null)
				{
					return obj2;
				}
			}
			object localPlayer = LocalPlayer;
			if (localPlayer != null)
			{
				object obj3 = ReflectionUtil.GetField(localPlayer, "m_playerProfile") ?? ReflectionUtil.GetField(localPlayer, "m_profile");
				if (obj3 != null)
				{
					return obj3;
				}
			}
			foreach (object item in FindLoadedObjects("Game"))
			{
				if (item == null)
				{
					continue;
				}
				MethodInfo methodInfo2 = ReflectionUtil.Method(item.GetType(), "GetPlayerProfile", 0, false);
				if (methodInfo2 != null)
				{
					object obj4 = methodInfo2.Invoke(item, null);
					if (obj4 != null)
					{
						return obj4;
					}
				}
				object obj5 = ReflectionUtil.GetField(item, "m_playerProfile") ?? ReflectionUtil.GetField(item, "m_profile");
				if (obj5 != null)
				{
					return obj5;
				}
			}
			return null;
		}

		internal static bool ProfileUsedCheats(object profile)
		{
			object field = ReflectionUtil.GetField(profile, "m_usedCheats");
			bool flag = default(bool);
			int num;
			if (field is bool)
			{
				flag = (bool)field;
				num = 1;
			}
			else
			{
				num = 0;
			}
			return (byte)((uint)num & (flag ? 1u : 0u)) != 0;
		}

		internal static bool SetProfileUsedCheats(object profile, bool value)
		{
			return ReflectionUtil.SetField(profile, "m_usedCheats", value);
		}

		internal static void ResetCheatStat(object profile)
		{
			if (profile == null || PlayerStatType == null)
			{
				return;
			}
			try
			{
				object obj = Enum.Parse(PlayerStatType, "Cheats");
				profile.GetType().GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault((MethodInfo m) => m.Name == "SetStat" && m.GetParameters().Length == 3)?.Invoke(profile, new object[3] { obj, 0f, false });
			}
			catch (Exception ex)
			{
				ManualLogSource modLog = CheatCleanupPlugin.ModLog;
				if (modLog != null)
				{
					modLog.LogWarning((object)("Nao foi possivel zerar a estatistica historica Cheats: " + ex.Message));
				}
			}
		}

		internal static object GetLocalPlayerInventory()
		{
			object localPlayer = LocalPlayer;
			return ((localPlayer == null) ? null : ReflectionUtil.Method(localPlayer.GetType(), "GetInventory", 0, false))?.Invoke(localPlayer, null);
		}

		internal static List<object> GetAllZdos()
		{
			List<object> list = new List<object>();
			object field = ReflectionUtil.GetField(ZdoManInstance, "m_objectsByID");
			if (field == null)
			{
				return list;
			}
			foreach (object item in ReflectionUtil.Enumerate(field))
			{
				object obj = ReflectionUtil.DictionaryValue(item);
				if (obj != null)
				{
					list.Add(obj);
				}
			}
			return list;
		}

		internal static bool GetZdoBool(object zdo, int hash)
		{
			if (zdo == null || hash == 0 || ZdoGetBoolMethod == null)
			{
				return false;
			}
			object obj = ZdoGetBoolMethod.Invoke(zdo, new object[2] { hash, false });
			bool flag = default(bool);
			int num;
			if (obj is bool)
			{
				flag = (bool)obj;
				num = 1;
			}
			else
			{
				num = 0;
			}
			return (byte)((uint)num & (flag ? 1u : 0u)) != 0;
		}

		internal static void RemoveZdoBool(object zdo, int hash)
		{
			if (zdo != null && hash != 0)
			{
				ZdoRemoveBoolMethod?.Invoke(zdo, new object[1] { hash });
			}
		}

		internal static byte[] GetZdoBytes(object zdo, int hash)
		{
			if (zdo == null || hash == 0 || ZdoGetBytesMethod == null)
			{
				return null;
			}
			return ZdoGetBytesMethod.Invoke(zdo, new object[2] { hash, null }) as byte[];
		}

		internal static void SetZdoBytes(object zdo, int hash, byte[] bytes)
		{
			if (zdo != null && hash != 0 && bytes != null)
			{
				ZdoSetBytesMethod?.Invoke(zdo, new object[2] { hash, bytes });
			}
		}

		internal static int StableHash(string value)
		{
			if (_stableHashMethod == null)
			{
				_stableHashMethod = FindStableHashMethod();
			}
			if (_stableHashMethod == null)
			{
				throw new MissingMethodException("GetStableHashCode(string) nao encontrado.");
			}
			return (int)_stableHashMethod.Invoke(null, new object[1] { value });
		}

		internal static object NewPackage(byte[] bytes = null)
		{
			if (ZPackageType == null)
			{
				throw new InvalidOperationException("Tipo ZPackage nao encontrado.");
			}
			if (bytes != null)
			{
				return Activator.CreateInstance(ZPackageType, bytes);
			}
			return Activator.CreateInstance(ZPackageType);
		}

		internal static int PackageReadInt(object package)
		{
			return (int)ReflectionUtil.Method(ZPackageType, "ReadInt", 0, false).Invoke(package, null);
		}

		internal static void PackageWriteInt(object package, int value)
		{
			ReflectionUtil.Method(ZPackageType, "Write", typeof(int)).Invoke(package, new object[1] { value });
		}

		internal static byte[] PackageGetArray(object package)
		{
			return ReflectionUtil.Method(ZPackageType, "GetArray", 0, false).Invoke(package, null) as byte[];
		}

		internal static object NewTemporaryInventory()
		{
			if (InventoryType == null)
			{
				throw new InvalidOperationException("Tipo Inventory nao encontrado.");
			}
			ConstructorInfo? constructorInfo = InventoryType.GetConstructors(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault(delegate(ConstructorInfo c)
			{
				ParameterInfo[] parameters = c.GetParameters();
				return parameters.Length == 4 && parameters[0].ParameterType == typeof(string) && parameters[2].ParameterType == typeof(int) && parameters[3].ParameterType == typeof(int);
			});
			if (constructorInfo == null)
			{
				throw new MissingMethodException("Construtor Inventory(string, Sprite, int, int) nao encontrado.");
			}
			return constructorInfo.Invoke(new object[4] { "CheatCleanup.Temp", null, 1024, 1024 });
		}

		internal static void InventoryLoad(object inventory, object package)
		{
			ReflectionUtil.Method(InventoryType, "Load", ZPackageType).Invoke(inventory, new object[1] { package });
		}

		internal static void InventorySave(object inventory, object package)
		{
			ReflectionUtil.Method(InventoryType, "Save", ZPackageType).Invoke(inventory, new object[1] { package });
		}

		internal static IEnumerable<object> InventoryItems(object inventory)
		{
			if (inventory == null)
			{
				yield break;
			}
			object sequence = ReflectionUtil.Method(inventory.GetType(), "GetAllItems", 0, false)?.Invoke(inventory, null);
			foreach (object item in ReflectionUtil.Enumerate(sequence))
			{
				yield return item;
			}
		}

		internal static bool ItemIsCheated(object item)
		{
			object field = ReflectionUtil.GetField(item, "m_cheated");
			bool flag = default(bool);
			int num;
			if (field is bool)
			{
				flag = (bool)field;
				num = 1;
			}
			else
			{
				num = 0;
			}
			return (byte)((uint)num & (flag ? 1u : 0u)) != 0;
		}

		internal static bool SetItemCheated(object item, bool value)
		{
			return ReflectionUtil.SetField(item, "m_cheated", value);
		}

		internal static object LoadDirectItemData(byte[] bytes, out int sourceVersion)
		{
			sourceVersion = 0;
			if (bytes == null || bytes.Length == 0 || ItemDataType == null)
			{
				return null;
			}
			object obj = NewPackage(bytes);
			sourceVersion = PackageReadInt(obj);
			MethodInfo? methodInfo = ItemDataType.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault(delegate(MethodInfo m)
			{
				ParameterInfo[] parameters = m.GetParameters();
				return m.Name == "Load" && parameters.Length == 2 && parameters[0].ParameterType == ZPackageType && parameters[1].ParameterType == typeof(int);
			});
			if (methodInfo == null)
			{
				throw new MissingMethodException("ItemData.Load(ZPackage,int) nao encontrado.");
			}
			object obj2 = methodInfo.Invoke(null, new object[2] { obj, sourceVersion });
			if (obj2 == null)
			{
				return null;
			}
			if (ItemDataType.IsInstanceOfType(obj2))
			{
				return obj2;
			}
			return obj2.GetType().GetField("Item1", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(obj2);
		}

		internal static byte[] SaveDirectItemData(object item)
		{
			if (item == null)
			{
				return null;
			}
			object obj = NewPackage();
			PackageWriteInt(obj, 109);
			MethodInfo methodInfo = ReflectionUtil.Method(item.GetType(), "Save", ZPackageType);
			if (methodInfo == null)
			{
				throw new MissingMethodException("ItemData.Save(ZPackage) nao encontrado.");
			}
			methodInfo.Invoke(item, new object[1] { obj });
			return PackageGetArray(obj);
		}

		internal static bool WorldContainsCheatedModifiers(object world)
		{
			if (world == null || WorldOptionsType == null)
			{
				return false;
			}
			MethodInfo methodInfo = ReflectionUtil.Method(WorldOptionsType, "WorldContainsCheatedModifiers", 1, true);
			bool flag = default(bool);
			int num;
			if (methodInfo != null)
			{
				object obj = methodInfo.Invoke(null, new object[1] { world });
				if (obj is bool)
				{
					flag = (bool)obj;
					num = 1;
				}
				else
				{
					num = 0;
				}
			}
			else
			{
				num = 0;
			}
			return (byte)((uint)num & (flag ? 1u : 0u)) != 0;
		}

		internal static IReadOnlyCollection<string> GetAllowedWorldKeys()
		{
			if (WorldOptionsType == null)
			{
				return null;
			}
			MethodInfo methodInfo = ReflectionUtil.Method(WorldOptionsType, "GetPossibleGlobalKeyValues", 0, true);
			if (methodInfo == null)
			{
				return null;
			}
			return (from s in ReflectionUtil.Enumerate(methodInfo.Invoke(null, null)).Select(Convert.ToString)
				where !string.IsNullOrEmpty(s)
				select s).ToList();
		}

		internal static object GetStartingWorldKeys(object world)
		{
			return ReflectionUtil.GetField(world, "m_startingGlobalKeys");
		}

		internal static bool RemoveWorldKey(object keyCollection, object key)
		{
			if (keyCollection == null)
			{
				return false;
			}
			MethodInfo methodInfo = keyCollection.GetType().GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault((MethodInfo m) => m.Name == "Remove" && m.GetParameters().Length == 1);
			if (methodInfo == null)
			{
				return false;
			}
			object obj = methodInfo.Invoke(keyCollection, new object[1] { key });
			bool flag = default(bool);
			int num;
			if (obj is bool)
			{
				flag = (bool)obj;
				num = ((1 == 0) ? 1 : 0);
			}
			else
			{
				num = 1;
			}
			return (byte)((uint)num | (flag ? 1u : 0u)) != 0;
		}

		internal static bool RequestNativeSave()
		{
			object zNetInstance = ZNetInstance;
			MethodInfo methodInfo = ReflectionUtil.Method(ZNetType, "SaveWorldAndPlayerProfiles", 0, false);
			if (zNetInstance == null || methodInfo == null)
			{
				return false;
			}
			methodInfo.Invoke(zNetInstance, null);
			return true;
		}

		internal static IEnumerable<object> FindLoadedObjects(string typeName)
		{
			Type type = ReflectionUtil.FindType(typeName);
			if (type == null)
			{
				yield break;
			}
			Type type2 = ReflectionUtil.FindType("UnityEngine.Object");
			if (type2 == null || !type2.IsAssignableFrom(type))
			{
				yield break;
			}
			Type type3 = ReflectionUtil.FindType("UnityEngine.Resources") ?? ReflectionUtil.FindType("Resources");
			if (type3 == null)
			{
				yield break;
			}
			MethodInfo methodInfo = type3.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault(delegate(MethodInfo m)
			{
				ParameterInfo[] parameters = m.GetParameters();
				return m.Name == "FindObjectsOfTypeAll" && !m.IsGenericMethod && parameters.Length == 1 && parameters[0].ParameterType == typeof(Type);
			});
			if (methodInfo == null)
			{
				yield break;
			}
			object sequence = methodInfo.Invoke(null, new object[1] { type });
			foreach (object item in ReflectionUtil.Enumerate(sequence))
			{
				yield return item;
			}
		}

		private static object GetStaticMember(Type type, string name)
		{
			if (type == null)
			{
				return null;
			}
			FieldInfo field = type.GetField(name, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
			if (field != null)
			{
				return field.GetValue(null);
			}
			PropertyInfo property = type.GetProperty(name, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
			if (property != null && property.GetIndexParameters().Length == 0)
			{
				return property.GetValue(null, null);
			}
			return null;
		}

		private static MethodInfo FindZdoMethod(string name, params Type[] parameterTypes)
		{
			return ZdoType?.GetMethod(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, parameterTypes, null);
		}

		private static MethodInfo FindStableHashMethod()
		{
			Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
			foreach (Assembly assembly in assemblies)
			{
				Type[] array;
				try
				{
					array = assembly.GetTypes();
				}
				catch (ReflectionTypeLoadException ex)
				{
					array = ex.Types?.Where((Type t) => t != null).ToArray() ?? Array.Empty<Type>();
				}
				catch
				{
					continue;
				}
				Type[] array2 = array;
				for (int num = 0; num < array2.Length; num++)
				{
					MethodInfo methodInfo = array2[num].GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault(delegate(MethodInfo m)
					{
						ParameterInfo[] parameters = m.GetParameters();
						return m.Name == "GetStableHashCode" && m.ReturnType == typeof(int) && parameters.Length == 1 && parameters[0].ParameterType == typeof(string);
					});
					if (methodInfo != null)
					{
						return methodInfo;
					}
				}
			}
			return null;
		}

		private static Type FindItemDataType()
		{
			Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
			for (int i = 0; i < assemblies.Length; i++)
			{
				Type type = assemblies[i].GetType("ItemDrop+ItemData", throwOnError: false);
				if (type != null)
				{
					return type;
				}
			}
			return ReflectionUtil.FindType("ItemData");
		}
	}
}