Decompiled source of ResourceDropModifier v0.1.0

ResourceDropModifier.dll

Decompiled a day ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using MushroomMods;
using MushroomSync;
using SoftReferenceableAssets;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyCompany("ResourceDropModifier")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("Per-item drop multipliers for Valheim, one config setting per item grouped by biome, synced from the server.")]
[assembly: AssemblyFileVersion("0.1.0.0")]
[assembly: AssemblyInformationalVersion("0.1.0+7757ccc54aeca6be4b442e05fcdfb2495dea3f50")]
[assembly: AssemblyProduct("Resource Drop Modifier")]
[assembly: AssemblyTitle("ResourceDropModifier")]
[assembly: AssemblyVersion("0.1.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 MushroomMods
{
	internal static class PatchIsolation
	{
		internal static int PatchAllIsolated(Harmony harmony, Assembly assembly, ManualLogSource log)
		{
			int num = 0;
			Type[] typesFromAssembly = AccessTools.GetTypesFromAssembly(assembly);
			foreach (Type type in typesFromAssembly)
			{
				try
				{
					harmony.CreateClassProcessor(type).Patch();
				}
				catch (Exception ex)
				{
					num++;
					log.LogError((object)("Harmony patch class " + type.FullName + " was skipped and the rest of " + harmony.Id + " is still active. " + ex));
				}
			}
			return num;
		}
	}
}
namespace ResourceDropModifier
{
	internal static class ConsoleCommands
	{
		[CompilerGenerated]
		private static class <>O
		{
			public static ConsoleEvent <0>__Reload;

			public static ConsoleEvent <1>__Rescan;

			public static ConsoleEvent <2>__Show;
		}

		private static bool _registered;

		internal static void Register()
		{
			//IL_003d: 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_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Expected O, but got Unknown
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Expected O, but got Unknown
			//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_0092: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: Expected O, but got Unknown
			if (_registered)
			{
				return;
			}
			_registered = true;
			try
			{
				object obj = <>O.<0>__Reload;
				if (obj == null)
				{
					ConsoleEvent val = Reload;
					<>O.<0>__Reload = val;
					obj = (object)val;
				}
				new ConsoleCommand("rdm_reload", "re-read the Resource Drop Modifier config from disk and apply it", (ConsoleEvent)obj, false, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false);
				object obj2 = <>O.<1>__Rescan;
				if (obj2 == null)
				{
					ConsoleEvent val2 = Rescan;
					<>O.<1>__Rescan = val2;
					obj2 = (object)val2;
				}
				new ConsoleCommand("rdm_rescan", "re-run the Resource Drop Modifier catalog walk and add any new items to the config", (ConsoleEvent)obj2, false, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false);
				object obj3 = <>O.<2>__Show;
				if (obj3 == null)
				{
					ConsoleEvent val3 = Show;
					<>O.<2>__Show = val3;
					obj3 = (object)val3;
				}
				new ConsoleCommand("rdm_show", "rdm_show <prefab> - print the drop multiplier in force for an item", (ConsoleEvent)obj3, false, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false);
				Plugin.Log.LogInfo((object)"Console commands registered: rdm_reload, rdm_rescan, rdm_show");
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)("Failed to register Resource Drop Modifier console commands; continuing without them: " + ex.Message));
			}
		}

		private static void Reload(ConsoleEventArgs args)
		{
			Plugin instance = Plugin.Instance;
			if (!((Object)(object)instance == (Object)null))
			{
				string text = instance.ReloadFromDisk();
				Terminal context = args.Context;
				if (context != null)
				{
					context.AddString(text);
				}
			}
		}

		private static void Rescan(ConsoleEventArgs args)
		{
			Plugin instance = Plugin.Instance;
			if ((Object)(object)instance == (Object)null)
			{
				return;
			}
			if ((Object)(object)ZoneSystem.instance == (Object)null)
			{
				Terminal context = args.Context;
				if (context != null)
				{
					context.AddString("<color=red>No world loaded - join a world first.</color>");
				}
				return;
			}
			instance.OnWorldReady();
			Terminal context2 = args.Context;
			if (context2 != null)
			{
				context2.AddString("Catalog rebuilt: " + DropConfig.Count + " item setting(s).");
			}
		}

		private static void Show(ConsoleEventArgs args)
		{
			if (args.Args == null || args.Args.Length < 2)
			{
				Terminal context = args.Context;
				if (context != null)
				{
					context.AddString("Usage: rdm_show <prefab>");
				}
				return;
			}
			string text = args.Args[1];
			float multiplier;
			bool flag = Plugin.Table.TryGet(text, out multiplier);
			string text2 = ((Plugin.Sync != null && Plugin.Sync.ClientSyncActive) ? "server" : "local");
			Terminal context2 = args.Context;
			if (context2 != null)
			{
				context2.AddString(flag ? (text + " = " + multiplier + " (" + text2 + ")") : (text + " has no setting; vanilla amounts (" + text2 + ")"));
			}
		}
	}
	internal sealed class CatalogEntry
	{
		internal string PrefabName;

		internal string DisplayName;

		internal Biome Biomes;

		internal bool Scalable;

		internal readonly SortedSet<string> Sources = new SortedSet<string>(StringComparer.Ordinal);

		internal Biome Section
		{
			get
			{
				//IL_000d: Unknown result type (might be due to invalid IL or missing references)
				//IL_000f: Unknown result type (might be due to invalid IL or missing references)
				//IL_0014: Unknown result type (might be due to invalid IL or missing references)
				//IL_0015: Unknown result type (might be due to invalid IL or missing references)
				//IL_0018: Unknown result type (might be due to invalid IL or missing references)
				Biome[] biomeOrder = DropCatalog.BiomeOrder;
				foreach (Biome val in biomeOrder)
				{
					if ((Biomes & val) != 0)
					{
						return val;
					}
				}
				return (Biome)0;
			}
		}
	}
	internal static class DropCatalog
	{
		private sealed class Walk
		{
			private const int MaxSourcesPerItem = 8;

			internal readonly Dictionary<string, CatalogEntry> Entries = new Dictionary<string, CatalogEntry>(StringComparer.Ordinal);

			private readonly ManualLogSource _log;

			private readonly HashSet<GameObject> _visited = new HashSet<GameObject>();

			internal Walk(ManualLogSource log)
			{
				_log = log;
			}

			internal int SpawnLists()
			{
				//IL_0063: Unknown result type (might be due to invalid IL or missing references)
				int num = 0;
				SpawnSystemList[] array = Resources.FindObjectsOfTypeAll<SpawnSystemList>();
				foreach (SpawnSystemList val in array)
				{
					if ((Object)(object)val == (Object)null || val.m_spawners == null)
					{
						continue;
					}
					foreach (SpawnData spawner in val.m_spawners)
					{
						if (spawner != null && !((Object)(object)spawner.m_prefab == (Object)null) && spawner.m_enabled)
						{
							num++;
							Root(spawner.m_prefab, spawner.m_biome);
						}
					}
				}
				return num;
			}

			internal int Vegetation()
			{
				//IL_0056: Unknown result type (might be due to invalid IL or missing references)
				int num = 0;
				ZoneSystem instance = ZoneSystem.instance;
				if ((Object)(object)instance == (Object)null || instance.m_vegetation == null)
				{
					return 0;
				}
				foreach (ZoneVegetation item in instance.m_vegetation)
				{
					if (item != null && !((Object)(object)item.m_prefab == (Object)null) && item.m_enable)
					{
						num++;
						Root(item.m_prefab, item.m_biome);
					}
				}
				return num;
			}

			internal int Locations()
			{
				//IL_005f: Unknown result type (might be due to invalid IL or missing references)
				int num = 0;
				ZoneSystem instance = ZoneSystem.instance;
				if ((Object)(object)instance == (Object)null || instance.m_locations == null)
				{
					return 0;
				}
				foreach (ZoneLocation location in instance.m_locations)
				{
					if (location == null || !location.m_enable)
					{
						continue;
					}
					bool loadedHere;
					GameObject val = Acquire(ref location.m_prefab, out loadedHere);
					if ((Object)(object)val == (Object)null)
					{
						continue;
					}
					try
					{
						num++;
						Root(val, location.m_biome);
					}
					finally
					{
						if (loadedHere)
						{
							location.m_prefab.Release();
						}
					}
				}
				return num;
			}

			private GameObject Acquire(ref SoftReference<GameObject> reference, out bool loadedHere)
			{
				//IL_0016: Unknown result type (might be due to invalid IL or missing references)
				loadedHere = false;
				if (!reference.IsValid)
				{
					return null;
				}
				if (!reference.IsLoaded)
				{
					try
					{
						if ((int)reference.Load() != 0)
						{
							return null;
						}
					}
					catch (Exception ex)
					{
						_log.LogWarning((object)("Drop catalog: could not load " + reference.Name + ": " + ex.Message));
						return null;
					}
					loadedHere = true;
				}
				return reference.Asset;
			}

			private void Root(GameObject prefab, Biome biomes)
			{
				//IL_000d: Unknown result type (might be due to invalid IL or missing references)
				_visited.Clear();
				Follow(prefab, biomes);
			}

			private void Follow(GameObject prefab, Biome biomes)
			{
				//IL_001b: Unknown result type (might be due to invalid IL or missing references)
				if ((Object)(object)prefab == (Object)null || !_visited.Add(prefab))
				{
					return;
				}
				try
				{
					Collect(prefab, biomes);
				}
				catch (Exception ex)
				{
					_log.LogWarning((object)("Drop catalog: skipped " + ((Object)prefab).name + ": " + ex.Message));
				}
			}

			private void Collect(GameObject root, Biome biomes)
			{
				//IL_0080: Unknown result type (might be due to invalid IL or missing references)
				//IL_002f: Unknown result type (might be due to invalid IL or missing references)
				//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
				//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
				//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
				//IL_0109: Unknown result type (might be due to invalid IL or missing references)
				//IL_0135: Unknown result type (might be due to invalid IL or missing references)
				//IL_0168: Unknown result type (might be due to invalid IL or missing references)
				//IL_019b: Unknown result type (might be due to invalid IL or missing references)
				//IL_01b7: Unknown result type (might be due to invalid IL or missing references)
				//IL_0201: Unknown result type (might be due to invalid IL or missing references)
				//IL_0292: Unknown result type (might be due to invalid IL or missing references)
				//IL_022a: Unknown result type (might be due to invalid IL or missing references)
				//IL_022f: Unknown result type (might be due to invalid IL or missing references)
				//IL_0231: Unknown result type (might be due to invalid IL or missing references)
				//IL_02c5: Unknown result type (might be due to invalid IL or missing references)
				//IL_0241: Unknown result type (might be due to invalid IL or missing references)
				//IL_024d: Unknown result type (might be due to invalid IL or missing references)
				//IL_030c: Unknown result type (might be due to invalid IL or missing references)
				//IL_0354: Unknown result type (might be due to invalid IL or missing references)
				//IL_0388: Unknown result type (might be due to invalid IL or missing references)
				//IL_03bb: Unknown result type (might be due to invalid IL or missing references)
				//IL_0452: Unknown result type (might be due to invalid IL or missing references)
				//IL_047e: Unknown result type (might be due to invalid IL or missing references)
				//IL_040d: Unknown result type (might be due to invalid IL or missing references)
				//IL_04f2: Unknown result type (might be due to invalid IL or missing references)
				//IL_04b2: Unknown result type (might be due to invalid IL or missing references)
				CharacterDrop[] componentsInChildren = root.GetComponentsInChildren<CharacterDrop>(true);
				foreach (CharacterDrop val in componentsInChildren)
				{
					foreach (Drop drop in val.m_drops)
					{
						Add(drop.m_prefab, biomes, ((Component)val).gameObject, drop.m_dontScale);
					}
				}
				DropOnDestroyed[] componentsInChildren2 = root.GetComponentsInChildren<DropOnDestroyed>(true);
				foreach (DropOnDestroyed val2 in componentsInChildren2)
				{
					AddTable(val2.m_dropWhenDestroyed, biomes, ((Component)val2).gameObject);
				}
				TreeBase[] componentsInChildren3 = root.GetComponentsInChildren<TreeBase>(true);
				foreach (TreeBase val3 in componentsInChildren3)
				{
					AddTable(val3.m_dropWhenDestroyed, biomes, ((Component)val3).gameObject);
					Follow(val3.m_logPrefab, biomes);
				}
				TreeLog[] componentsInChildren4 = root.GetComponentsInChildren<TreeLog>(true);
				foreach (TreeLog val4 in componentsInChildren4)
				{
					AddTable(val4.m_dropWhenDestroyed, biomes, ((Component)val4).gameObject);
					Follow(val4.m_subLogPrefab, biomes);
				}
				MineRock[] componentsInChildren5 = root.GetComponentsInChildren<MineRock>(true);
				foreach (MineRock val5 in componentsInChildren5)
				{
					AddTable(val5.m_dropItems, biomes, ((Component)val5).gameObject);
				}
				MineRock5[] componentsInChildren6 = root.GetComponentsInChildren<MineRock5>(true);
				foreach (MineRock5 val6 in componentsInChildren6)
				{
					AddTable(val6.m_dropItems, biomes, ((Component)val6).gameObject);
				}
				Pickable[] componentsInChildren7 = root.GetComponentsInChildren<Pickable>(true);
				foreach (Pickable val7 in componentsInChildren7)
				{
					Add(val7.m_itemPrefab, biomes, ((Component)val7).gameObject, val7.m_dontScale);
					AddTable(val7.m_extraDrops, biomes, ((Component)val7).gameObject);
				}
				PickableItem[] componentsInChildren8 = root.GetComponentsInChildren<PickableItem>(true);
				foreach (PickableItem val8 in componentsInChildren8)
				{
					if ((Object)(object)val8.m_itemPrefab != (Object)null)
					{
						Add(((Component)val8.m_itemPrefab).gameObject, biomes, ((Component)val8).gameObject, dontScale: false);
					}
					if (val8.m_randomItemPrefabs == null)
					{
						continue;
					}
					RandomItem[] randomItemPrefabs = val8.m_randomItemPrefabs;
					foreach (RandomItem val9 in randomItemPrefabs)
					{
						if ((Object)(object)val9.m_itemPrefab != (Object)null)
						{
							Add(((Component)val9.m_itemPrefab).gameObject, biomes, ((Component)val8).gameObject, dontScale: false);
						}
					}
				}
				Container[] componentsInChildren9 = root.GetComponentsInChildren<Container>(true);
				foreach (Container val10 in componentsInChildren9)
				{
					AddTable(val10.m_defaultItems, biomes, ((Component)val10).gameObject);
				}
				LootSpawner[] componentsInChildren10 = root.GetComponentsInChildren<LootSpawner>(true);
				foreach (LootSpawner val11 in componentsInChildren10)
				{
					AddTable(val11.m_items, biomes, ((Component)val11).gameObject);
				}
				Beehive[] componentsInChildren11 = root.GetComponentsInChildren<Beehive>(true);
				foreach (Beehive val12 in componentsInChildren11)
				{
					if ((Object)(object)val12.m_honeyItem != (Object)null)
					{
						Add(((Component)val12.m_honeyItem).gameObject, biomes, ((Component)val12).gameObject, dontScale: false);
					}
				}
				SapCollector[] componentsInChildren12 = root.GetComponentsInChildren<SapCollector>(true);
				foreach (SapCollector val13 in componentsInChildren12)
				{
					if ((Object)(object)val13.m_spawnItem != (Object)null)
					{
						Add(((Component)val13.m_spawnItem).gameObject, biomes, ((Component)val13).gameObject, dontScale: false);
					}
				}
				Fish[] componentsInChildren13 = root.GetComponentsInChildren<Fish>(true);
				foreach (Fish val14 in componentsInChildren13)
				{
					AddTable(val14.m_extraDrops, biomes, ((Component)val14).gameObject);
				}
				CreatureSpawner[] componentsInChildren14 = root.GetComponentsInChildren<CreatureSpawner>(true);
				foreach (CreatureSpawner val15 in componentsInChildren14)
				{
					Follow(val15.m_creaturePrefab, biomes);
				}
				SpawnArea[] componentsInChildren15 = root.GetComponentsInChildren<SpawnArea>(true);
				foreach (SpawnArea val16 in componentsInChildren15)
				{
					if (val16.m_prefabs == null)
					{
						continue;
					}
					foreach (SpawnData prefab in val16.m_prefabs)
					{
						if (prefab != null)
						{
							Follow(prefab.m_prefab, biomes);
						}
					}
				}
				Destructible[] componentsInChildren16 = root.GetComponentsInChildren<Destructible>(true);
				foreach (Destructible val17 in componentsInChildren16)
				{
					Follow(val17.m_spawnWhenDestroyed, biomes);
				}
				Growup[] componentsInChildren17 = root.GetComponentsInChildren<Growup>(true);
				foreach (Growup val18 in componentsInChildren17)
				{
					Follow(val18.m_grownPrefab, biomes);
					if (val18.m_altGrownPrefabs == null)
					{
						continue;
					}
					foreach (GrownEntry altGrownPrefab in val18.m_altGrownPrefabs)
					{
						if (altGrownPrefab != null)
						{
							Follow(altGrownPrefab.m_prefab, biomes);
						}
					}
				}
				DungeonGenerator[] componentsInChildren18 = root.GetComponentsInChildren<DungeonGenerator>(true);
				foreach (DungeonGenerator generator in componentsInChildren18)
				{
					Rooms(generator, biomes);
				}
			}

			private void Rooms(DungeonGenerator generator, Biome biomes)
			{
				//IL_002c: Unknown result type (might be due to invalid IL or missing references)
				//IL_0032: Unknown result type (might be due to invalid IL or missing references)
				//IL_0037: Unknown result type (might be due to invalid IL or missing references)
				//IL_0055: Unknown result type (might be due to invalid IL or missing references)
				List<RoomData> rooms;
				try
				{
					rooms = DungeonDB.GetRooms();
				}
				catch (Exception)
				{
					return;
				}
				if (rooms == null)
				{
					return;
				}
				foreach (RoomData item in rooms)
				{
					if (item == null || !item.m_enabled || (item.m_theme & generator.m_themes) == 0)
					{
						continue;
					}
					bool loadedHere;
					GameObject val = Acquire(ref item.m_prefab, out loadedHere);
					if ((Object)(object)val == (Object)null)
					{
						continue;
					}
					try
					{
						Follow(val, biomes);
					}
					finally
					{
						if (loadedHere)
						{
							item.m_prefab.Release();
						}
					}
				}
			}

			private void AddTable(DropTable table, Biome biomes, GameObject source)
			{
				//IL_001c: Unknown result type (might be due to invalid IL or missing references)
				//IL_0021: Unknown result type (might be due to invalid IL or missing references)
				//IL_0023: Unknown result type (might be due to invalid IL or missing references)
				//IL_0029: Unknown result type (might be due to invalid IL or missing references)
				//IL_002b: Unknown result type (might be due to invalid IL or missing references)
				if (table == null || table.m_drops == null)
				{
					return;
				}
				foreach (DropData drop in table.m_drops)
				{
					Add(drop.m_item, biomes, source, drop.m_dontScale);
				}
			}

			private void Add(GameObject prefab, Biome biomes, GameObject source, bool dontScale)
			{
				//IL_001a: Unknown result type (might be due to invalid IL or missing references)
				//IL_005c: Unknown result type (might be due to invalid IL or missing references)
				//IL_0061: 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)
				//IL_0063: Unknown result type (might be due to invalid IL or missing references)
				if ((Object)(object)prefab == (Object)null)
				{
					return;
				}
				if ((Object)(object)prefab.GetComponent<ItemDrop>() == (Object)null)
				{
					Follow(prefab, biomes);
					return;
				}
				if (!Entries.TryGetValue(((Object)prefab).name, out var value))
				{
					value = new CatalogEntry
					{
						PrefabName = ((Object)prefab).name
					};
					Entries[((Object)prefab).name] = value;
				}
				CatalogEntry catalogEntry = value;
				catalogEntry.Biomes |= biomes;
				value.Scalable |= !dontScale;
				if ((Object)(object)source != (Object)null && value.Sources.Count < 8)
				{
					value.Sources.Add(SourceName(source));
				}
			}

			private static string SourceName(GameObject source)
			{
				string text = ((Object)source).name;
				if (text.EndsWith("(Clone)", StringComparison.Ordinal))
				{
					text = text.Substring(0, text.Length - "(Clone)".Length);
				}
				return text.Trim();
			}
		}

		internal static readonly Biome[] BiomeOrder;

		internal static List<CatalogEntry> Build(ManualLogSource log)
		{
			Walk walk = new Walk(log);
			Stopwatch stopwatch = Stopwatch.StartNew();
			int num = walk.SpawnLists();
			int num2 = walk.Vegetation();
			int num3 = walk.Locations();
			List<CatalogEntry> list = new List<CatalogEntry>(walk.Entries.Values);
			foreach (CatalogEntry item in list)
			{
				item.DisplayName = Localize(item.PrefabName);
			}
			log.LogInfo((object)("Drop catalog: " + list.Count + " item(s) from " + num + " spawner(s), " + num2 + " vegetation prefab(s) and " + num3 + " location(s) in " + stopwatch.ElapsedMilliseconds + " ms."));
			return list;
		}

		private static string Localize(string prefabName)
		{
			try
			{
				ObjectDB instance = ObjectDB.instance;
				GameObject val = (((Object)(object)instance != (Object)null) ? instance.GetItemPrefab(prefabName) : null);
				ItemDrop val2 = (((Object)(object)val != (Object)null) ? val.GetComponent<ItemDrop>() : null);
				string text = (((Object)(object)val2 != (Object)null && val2.m_itemData != null && val2.m_itemData.m_shared != null) ? val2.m_itemData.m_shared.m_name : null);
				if (string.IsNullOrEmpty(text))
				{
					return prefabName;
				}
				Localization instance2 = Localization.instance;
				string text2 = ((instance2 != null) ? instance2.Localize(text) : null);
				if (string.IsNullOrEmpty(text2) || text2.StartsWith("[", StringComparison.Ordinal))
				{
					return text.TrimStart(new char[1] { '$' });
				}
				return text2;
			}
			catch (Exception)
			{
				return prefabName;
			}
		}

		static DropCatalog()
		{
			Biome[] array = new Biome[9];
			RuntimeHelpers.InitializeArray(array, (RuntimeFieldHandle)/*OpCode not supported: LdMemberToken*/);
			BiomeOrder = (Biome[])(object)array;
		}
	}
	internal static class DropConfig
	{
		internal const float MaxMultiplier = 10f;

		private static readonly char[] InvalidKeyChars = new char[8] { '=', '\n', '\t', '\\', '"', '\'', '[', ']' };

		private static readonly Dictionary<string, ConfigEntry<float>> Entries = new Dictionary<string, ConfigEntry<float>>(StringComparer.Ordinal);

		internal static int Count => Entries.Count;

		internal unsafe static int BindAll(ConfigFile config, List<CatalogEntry> catalog, ManualLogSource log)
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
			Dictionary<Biome, List<CatalogEntry>> dictionary = new Dictionary<Biome, List<CatalogEntry>>();
			foreach (CatalogEntry item in catalog)
			{
				Biome section = item.Section;
				if ((int)section == 0)
				{
					log.LogInfo((object)("Drop catalog: " + item.PrefabName + " has no biome and is not listed."));
					continue;
				}
				if (!dictionary.TryGetValue(section, out var value))
				{
					value = (dictionary[section] = new List<CatalogEntry>());
				}
				value.Add(item);
			}
			int num = 0;
			bool saveOnConfigSet = config.SaveOnConfigSet;
			config.SaveOnConfigSet = false;
			try
			{
				Biome[] biomeOrder = DropCatalog.BiomeOrder;
				for (int i = 0; i < biomeOrder.Length; i++)
				{
					Biome key = biomeOrder[i];
					if (!dictionary.TryGetValue(key, out var value2))
					{
						continue;
					}
					value2.Sort((CatalogEntry a, CatalogEntry b) => string.CompareOrdinal(a.PrefabName, b.PrefabName));
					foreach (CatalogEntry item2 in value2)
					{
						if (Bind(config, ((object)(*(Biome*)(&key))/*cast due to .constrained prefix*/).ToString(), item2, log))
						{
							num++;
						}
					}
				}
			}
			finally
			{
				config.SaveOnConfigSet = saveOnConfigSet;
			}
			if (num > 0)
			{
				config.Save();
			}
			return num;
		}

		private static bool Bind(ConfigFile config, string section, CatalogEntry entry, ManualLogSource log)
		{
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Expected O, but got Unknown
			//IL_007a: Expected O, but got Unknown
			if (Entries.ContainsKey(entry.PrefabName))
			{
				return false;
			}
			if (entry.PrefabName.IndexOfAny(InvalidKeyChars) >= 0)
			{
				log.LogWarning((object)("Drop catalog: '" + entry.PrefabName + "' cannot be a config key and has no setting."));
				return false;
			}
			ConfigEntry<float> value = config.Bind<float>(new ConfigDefinition(section, entry.PrefabName), 1f, new ConfigDescription(Describe(entry), (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 10f), Array.Empty<object>()));
			Entries[entry.PrefabName] = value;
			return true;
		}

		private static string Describe(CatalogEntry entry)
		{
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.Append(entry.DisplayName ?? entry.PrefabName);
			if (entry.Sources.Count > 0)
			{
				stringBuilder.Append(". Drops from: ");
				stringBuilder.Append(string.Join(", ", entry.Sources));
			}
			stringBuilder.Append(". Vanilla amount times this; 0 removes the drop.");
			if (!entry.Scalable)
			{
				stringBuilder.Append(" Not scalable: vanilla exempts every drop of this item, so this setting does nothing.");
			}
			stringBuilder.Append(" While connected to a server, the server's value is the one in force.");
			return stringBuilder.ToString();
		}

		internal static MultiplierTable BuildTable()
		{
			Dictionary<string, float> dictionary = new Dictionary<string, float>(Entries.Count, StringComparer.Ordinal);
			foreach (KeyValuePair<string, ConfigEntry<float>> entry in Entries)
			{
				dictionary[entry.Key] = entry.Value.Value;
			}
			return new MultiplierTable(dictionary);
		}

		internal static bool IsItemSetting(ConfigEntryBase setting)
		{
			if (setting != null && Entries.TryGetValue(setting.Definition.Key, out var value))
			{
				return (object)value == setting;
			}
			return false;
		}
	}
	internal static class ItemIndex
	{
		private static Dictionary<SharedData, string> _bySharedData = new Dictionary<SharedData, string>();

		internal static int Count => _bySharedData.Count;

		internal static void Rebuild(ObjectDB db)
		{
			Dictionary<SharedData, string> dictionary = new Dictionary<SharedData, string>();
			if ((Object)(object)db != (Object)null)
			{
				foreach (GameObject item in db.m_items)
				{
					if (!((Object)(object)item == (Object)null))
					{
						ItemDrop component = item.GetComponent<ItemDrop>();
						if (!((Object)(object)component == (Object)null) && component.m_itemData != null && component.m_itemData.m_shared != null && !dictionary.ContainsKey(component.m_itemData.m_shared))
						{
							dictionary[component.m_itemData.m_shared] = ((Object)item).name;
						}
					}
				}
			}
			_bySharedData = dictionary;
		}

		internal static string NameOf(ItemData data)
		{
			if (data == null)
			{
				return null;
			}
			if ((Object)(object)data.m_dropPrefab != (Object)null)
			{
				return ((Object)data.m_dropPrefab).name;
			}
			if (data.m_shared != null && _bySharedData.TryGetValue(data.m_shared, out var value))
			{
				return value;
			}
			return null;
		}
	}
	internal sealed class MultiplierSync
	{
		private readonly SyncChannel _channel;

		private readonly ManualLogSource _log;

		internal MultiplierTable LocalTable = MultiplierTable.Empty;

		internal bool ClientSyncActive => _channel.ClientSyncActive;

		internal MultiplierSync(string pluginGuid, string pluginVersion, ManualLogSource log)
		{
			_log = log;
			_channel = SyncChannel.Create(pluginGuid + ".Multipliers", pluginVersion, log);
			_channel.WritePayload = Write;
			_channel.ReadPayload = Read;
			_channel.Cleared = OnCleared;
			_channel.SendGate = () => Plugin.LockConfiguration == null || Plugin.LockConfiguration.Value;
		}

		internal static bool IsServerAuthority()
		{
			return SyncChannel.IsServerAuthority();
		}

		internal void Start()
		{
			_channel.Start();
		}

		internal void Broadcast()
		{
			_channel.Broadcast();
		}

		private void Write(ZPackage pkg)
		{
			List<KeyValuePair<string, float>> list = new List<KeyValuePair<string, float>>();
			foreach (KeyValuePair<string, float> entry in Plugin.Table.Entries)
			{
				if (entry.Value != 1f)
				{
					list.Add(entry);
				}
			}
			pkg.Write(list.Count);
			foreach (KeyValuePair<string, float> item in list)
			{
				pkg.Write(item.Key);
				pkg.Write(item.Value);
			}
		}

		private void Read(ZPackage pkg)
		{
			int num = SyncChannel.ReadCount(pkg);
			Dictionary<string, float> dictionary = new Dictionary<string, float>(num);
			for (int i = 0; i < num; i++)
			{
				string key = pkg.ReadString();
				float value = pkg.ReadSingle();
				dictionary[key] = value;
			}
			Plugin.Table = new MultiplierTable(dictionary);
			_log.LogInfo((object)("Using " + num + " drop multiplier(s) from the server."));
		}

		private void OnCleared(string reason)
		{
			Plugin.Table = LocalTable;
			_log.LogInfo((object)("Server multipliers dropped (" + reason + "); using the local config."));
		}
	}
	internal sealed class MultiplierTable
	{
		internal static readonly MultiplierTable Empty = new MultiplierTable(new Dictionary<string, float>());

		private readonly Dictionary<string, float> _byPrefab;

		internal int Count => _byPrefab.Count;

		internal IEnumerable<KeyValuePair<string, float>> Entries => _byPrefab;

		internal MultiplierTable(Dictionary<string, float> byPrefab)
		{
			_byPrefab = new Dictionary<string, float>(byPrefab, StringComparer.Ordinal);
		}

		internal float Get(string prefabName)
		{
			if (prefabName != null && _byPrefab.TryGetValue(prefabName, out var value))
			{
				return value;
			}
			return 1f;
		}

		internal bool TryGet(string prefabName, out float multiplier)
		{
			multiplier = 1f;
			if (prefabName != null)
			{
				return _byPrefab.TryGetValue(prefabName, out multiplier);
			}
			return false;
		}

		internal static int Scale(int amount, float multiplier)
		{
			if (multiplier == 1f || amount <= 0)
			{
				return amount;
			}
			if (multiplier <= 0f)
			{
				return 0;
			}
			double num = (double)amount * (double)multiplier;
			int num2 = (int)Math.Floor(num);
			double num3 = num - (double)num2;
			if (num3 > 0.0 && (double)Random.value < num3)
			{
				num2++;
			}
			return num2;
		}
	}
	internal static class DropScaling
	{
		internal const int MaxObjectsPerDrop = 100;

		internal const int MaxListEntries = 200;

		internal static int Apply(ItemData data, int vanilla)
		{
			string text = ItemIndex.NameOf(data);
			if (text == null || !Plugin.Table.TryGet(text, out var multiplier) || multiplier == 1f)
			{
				return vanilla;
			}
			int num = MultiplierTable.Scale(vanilla, multiplier);
			int num2 = ((data.m_shared != null && data.m_shared.m_maxStackSize > 1) ? data.m_shared.m_maxStackSize : 100);
			if (num > num2)
			{
				num = num2;
			}
			Plugin.Debug("ScaleDrops " + text + ": " + vanilla + " x " + multiplier + " = " + num);
			return num;
		}
	}
	[HarmonyPatch(typeof(Game))]
	internal static class Game_ScaleDrops_Patch
	{
		[HarmonyPrefix]
		[HarmonyPatch("ScaleDrops", new Type[]
		{
			typeof(GameObject),
			typeof(int)
		})]
		internal static bool RouteSingle(Game __instance, GameObject drop, int amount, ref int __result)
		{
			if (Game.m_resourceRate != 1f || (Object)(object)drop == (Object)null)
			{
				return true;
			}
			ItemDrop component = drop.GetComponent<ItemDrop>();
			if ((Object)(object)component == (Object)null)
			{
				return true;
			}
			__result = __instance.ScaleDrops(component.m_itemData, amount);
			return false;
		}

		[HarmonyPrefix]
		[HarmonyPatch("ScaleDrops", new Type[]
		{
			typeof(GameObject),
			typeof(int),
			typeof(int)
		})]
		internal static bool RouteRange(Game __instance, GameObject drop, int randomMin, int randomMax, ref int __result)
		{
			if (Game.m_resourceRate != 1f || (Object)(object)drop == (Object)null)
			{
				return true;
			}
			ItemDrop component = drop.GetComponent<ItemDrop>();
			if ((Object)(object)component == (Object)null)
			{
				return true;
			}
			__result = __instance.ScaleDrops(component.m_itemData, randomMin, randomMax);
			return false;
		}

		[HarmonyPostfix]
		[HarmonyPatch("ScaleDrops", new Type[]
		{
			typeof(ItemData),
			typeof(int)
		})]
		internal static void ScaleSingle(ItemData data, ref int __result)
		{
			__result = DropScaling.Apply(data, __result);
		}

		[HarmonyPostfix]
		[HarmonyPatch("ScaleDrops", new Type[]
		{
			typeof(ItemData),
			typeof(int),
			typeof(int)
		})]
		internal static void ScaleRange(ItemData data, ref int __result)
		{
			__result = DropScaling.Apply(data, __result);
		}
	}
	[HarmonyPatch(typeof(DropTable), "GetDropList", new Type[] { })]
	internal static class DropTable_GetDropList_Patch
	{
		[HarmonyPostfix]
		internal static void Rescale(DropTable __instance, ref List<GameObject> __result)
		{
			if (__result == null || __result.Count == 0 || Plugin.Table.Count == 0)
			{
				return;
			}
			List<GameObject> list = new List<GameObject>();
			Dictionary<GameObject, int> dictionary = new Dictionary<GameObject, int>();
			foreach (GameObject item in __result)
			{
				if (!((Object)(object)item == (Object)null))
				{
					if (!dictionary.TryGetValue(item, out var value))
					{
						list.Add(item);
					}
					dictionary[item] = value + 1;
				}
			}
			bool flag = false;
			List<GameObject> list2 = new List<GameObject>(__result.Count);
			foreach (GameObject item2 in list)
			{
				int num = dictionary[item2];
				int num2 = num;
				if (!IsExempt(__instance, item2) && Plugin.Table.TryGet(((Object)item2).name, out var multiplier) && multiplier != 1f)
				{
					num2 = MultiplierTable.Scale(num, multiplier);
					if (num2 > 100)
					{
						num2 = 100;
					}
					flag = true;
					Plugin.Debug("GetDropList " + ((Object)item2).name + ": " + num + " x " + multiplier + " = " + num2);
				}
				for (int i = 0; i < num2; i++)
				{
					if (list2.Count >= 200)
					{
						break;
					}
					list2.Add(item2);
				}
			}
			if (flag)
			{
				__result = list2;
			}
		}

		private static bool IsExempt(DropTable table, GameObject prefab)
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			foreach (DropData drop in table.m_drops)
			{
				if ((Object)(object)drop.m_item == (Object)(object)prefab)
				{
					return drop.m_dontScale;
				}
			}
			return false;
		}
	}
	[HarmonyPatch(typeof(DropTable), "GetDropListItems")]
	internal static class DropTable_GetDropListItems_Patch
	{
		[HarmonyPostfix]
		internal static void DropEmptyStacks(List<ItemData> __result)
		{
			if (__result != null && __result.Count > 0)
			{
				__result.RemoveAll((ItemData item) => item == null || item.m_stack <= 0);
			}
		}
	}
	[HarmonyPatch(typeof(Pickable), "RPC_Pick")]
	internal static class Pickable_RPC_Pick_Patch
	{
		[HarmonyPrefix]
		internal static void LiftFloor(Pickable __instance, out int __state)
		{
			__state = __instance.m_minAmountScaled;
			if (!__instance.m_dontScale && (Object)(object)__instance.m_itemPrefab != (Object)null && Plugin.Table.TryGet(((Object)__instance.m_itemPrefab).name, out var multiplier) && multiplier < 1f)
			{
				__instance.m_minAmountScaled = 0;
			}
		}

		[HarmonyFinalizer]
		internal static Exception RestoreFloor(Pickable __instance, int __state, Exception __exception)
		{
			__instance.m_minAmountScaled = __state;
			return __exception;
		}
	}
	[HarmonyPatch(typeof(ZoneSystem), "Start")]
	internal static class ZoneSystem_Start_Patch
	{
		[HarmonyPostfix]
		internal static void BuildCatalog()
		{
			Plugin instance = Plugin.Instance;
			if ((Object)(object)instance != (Object)null)
			{
				instance.OnWorldReady();
			}
		}
	}
	[HarmonyPatch(typeof(Terminal), "InitTerminal")]
	internal static class Terminal_InitTerminal_Patch
	{
		[HarmonyPostfix]
		internal static void Register()
		{
			ConsoleCommands.Register();
		}
	}
	[BepInPlugin("Gonfreecss.ResourceDropModifier", "ResourceDropModifier", "0.1.0")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInProcess("valheim.exe")]
	[BepInProcess("valheim_server.exe")]
	public sealed class Plugin : BaseUnityPlugin
	{
		public const string PluginGuid = "Gonfreecss.ResourceDropModifier";

		public const string PluginName = "ResourceDropModifier";

		public const string PluginVersion = "0.1.0";

		private const float RebuildDelaySeconds = 1f;

		private const double IgnoreOwnWriteSeconds = 2.0;

		internal static Plugin Instance;

		internal static ManualLogSource Log;

		internal static ConfigEntry<bool> LockConfiguration;

		internal static ConfigEntry<bool> EnableDebugLogging;

		internal static MultiplierTable Table = MultiplierTable.Empty;

		internal static MultiplierSync Sync;

		private Harmony _harmony;

		private FileSystemWatcher _watcher;

		private volatile bool _fileChanged;

		private float _rebuildDue = -1f;

		private DateTime _lastOwnWrite = DateTime.MinValue;

		private void Awake()
		{
			//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b8: Expected O, but got Unknown
			Instance = this;
			Log = ((BaseUnityPlugin)this).Logger;
			LockConfiguration = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "LockConfiguration", true, "If on, the server owns the multipliers and connected clients use the server's values.");
			EnableDebugLogging = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "EnableDebugLogging", false, "Log every scaled drop. Noisy; for checking a multiplier is applied.");
			((BaseUnityPlugin)this).Config.SettingChanged += OnSettingChanged;
			((BaseUnityPlugin)this).Config.ConfigReloaded += OnConfigReloaded;
			StartWatcher();
			Sync = new MultiplierSync("Gonfreecss.ResourceDropModifier", "0.1.0", Log);
			Sync.Start();
			_harmony = new Harmony("Gonfreecss.ResourceDropModifier");
			int num = PatchIsolation.PatchAllIsolated(_harmony, typeof(Plugin).Assembly, Log);
			Log.LogInfo((object)("ResourceDropModifier 0.1.0 loaded." + ((num > 0) ? (" " + num + " patch class(es) skipped - see the errors above.") : "")));
		}

		private void OnDestroy()
		{
			_watcher?.Dispose();
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
		}

		private void Update()
		{
			if (_fileChanged)
			{
				_fileChanged = false;
				if ((DateTime.UtcNow - _lastOwnWrite).TotalSeconds > 2.0)
				{
					ReloadFromDisk();
				}
			}
			if (_rebuildDue >= 0f && Time.unscaledTime >= _rebuildDue)
			{
				_rebuildDue = -1f;
				RebuildTable();
			}
		}

		internal void OnWorldReady()
		{
			try
			{
				ItemIndex.Rebuild(ObjectDB.instance);
				List<CatalogEntry> catalog = DropCatalog.Build(Log);
				_lastOwnWrite = DateTime.UtcNow;
				int num = DropConfig.BindAll(((BaseUnityPlugin)this).Config, catalog, Log);
				if (num > 0)
				{
					Log.LogInfo((object)("Bound " + num + " item setting(s) in " + Path.GetFileName(((BaseUnityPlugin)this).Config.ConfigFilePath) + " (" + DropConfig.Count + " total)."));
				}
				RebuildTable();
			}
			catch (Exception ex)
			{
				Log.LogError((object)("Building the drop catalog failed; multipliers from the last successful build stay in force. " + ex));
			}
		}

		private void RebuildTable()
		{
			MultiplierTable multiplierTable = DropConfig.BuildTable();
			Sync.LocalTable = multiplierTable;
			if (MultiplierSync.IsServerAuthority())
			{
				Table = multiplierTable;
				Sync.Broadcast();
				Debug("Table rebuilt: " + multiplierTable.Count + " entries, " + CountChanged(multiplierTable) + " not at 1; broadcast.");
			}
			else if (!Sync.ClientSyncActive)
			{
				Table = multiplierTable;
				Debug("Table rebuilt from local config: " + multiplierTable.Count + " entries (no server table active).");
			}
		}

		private static int CountChanged(MultiplierTable table)
		{
			int num = 0;
			foreach (KeyValuePair<string, float> entry in table.Entries)
			{
				if (entry.Value != 1f)
				{
					num++;
				}
			}
			return num;
		}

		private void OnSettingChanged(object sender, SettingChangedEventArgs args)
		{
			if (args != null && args.ChangedSetting != null && (DropConfig.IsItemSetting(args.ChangedSetting) || (object)args.ChangedSetting == LockConfiguration))
			{
				_lastOwnWrite = DateTime.UtcNow;
				ScheduleRebuild();
			}
		}

		private void OnConfigReloaded(object sender, EventArgs args)
		{
			ScheduleRebuild();
		}

		private void ScheduleRebuild()
		{
			_rebuildDue = Time.unscaledTime + 1f;
		}

		internal string ReloadFromDisk()
		{
			try
			{
				((BaseUnityPlugin)this).Config.Reload();
				return "Reloaded " + Path.GetFileName(((BaseUnityPlugin)this).Config.ConfigFilePath) + "; applying in " + 1f + "s.";
			}
			catch (Exception ex)
			{
				Log.LogWarning((object)("Config reload failed; keeping the current values. " + ex.Message));
				return "<color=red>Reload failed: " + ex.Message + "</color>";
			}
		}

		private void StartWatcher()
		{
			try
			{
				string directoryName = Path.GetDirectoryName(((BaseUnityPlugin)this).Config.ConfigFilePath);
				string fileName = Path.GetFileName(((BaseUnityPlugin)this).Config.ConfigFilePath);
				if (!string.IsNullOrEmpty(directoryName) && !string.IsNullOrEmpty(fileName))
				{
					_watcher = new FileSystemWatcher(directoryName, fileName)
					{
						NotifyFilter = (NotifyFilters.FileName | NotifyFilters.Size | NotifyFilters.LastWrite)
					};
					_watcher.Changed += delegate
					{
						_fileChanged = true;
					};
					_watcher.Created += delegate
					{
						_fileChanged = true;
					};
					_watcher.Renamed += delegate
					{
						_fileChanged = true;
					};
					_watcher.EnableRaisingEvents = true;
				}
			}
			catch (Exception ex)
			{
				Log.LogWarning((object)("Could not watch the config file for changes (use rdm_reload instead): " + ex.Message));
				_watcher = null;
			}
		}

		internal static void Debug(string message)
		{
			if (EnableDebugLogging != null && EnableDebugLogging.Value)
			{
				Log.LogInfo((object)message);
			}
		}
	}
}