Decompiled source of ConfigurationsForEveryone v1.0.0

ConfigurationsForEveryone.dll

Decompiled 16 hours ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Jotunn.Entities;
using Jotunn.Managers;
using Microsoft.CodeAnalysis;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: AssemblyTitle("ConfigurationsForEveryone")]
[assembly: AssemblyDescription("Adds server-synced BepInEx configs for items, pieces and creatures of Jotunn-based content mods that ship without any")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("ConfigurationsForEveryone")]
[assembly: AssemblyCopyright("Copyright © 2026")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("b7e2c1d4-9a8f-4c3e-bd21-7f0a4e6d2c91")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[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.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[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 ConfigurationsForEveryone
{
	[BepInPlugin("IAmOnTheInternetAndItIsScary.ConfigurationsForEveryone", "ConfigurationsForEveryone", "1.0.0")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public class ConfigurationsForEveryonePlugin : BaseUnityPlugin
	{
		[HarmonyPatch(typeof(FejdStartup), "Awake")]
		private static class FejdStartupPatch
		{
			private static bool built;

			[HarmonyPostfix]
			private static void Postfix()
			{
				if (!built)
				{
					built = true;
					ConfigBuilder.BuildAll("FejdStartup.Awake");
				}
			}
		}

		internal const string ModName = "ConfigurationsForEveryone";

		internal const string ModGUID = "IAmOnTheInternetAndItIsScary.ConfigurationsForEveryone";

		internal const string ModVersion = "1.0.0";

		internal static readonly ManualLogSource Log = Logger.CreateLogSource("ConfigurationsForEveryone");

		internal static ConfigEntry<bool>? DebugLogging;

		private readonly Harmony _harmony = new Harmony("IAmOnTheInternetAndItIsScary.ConfigurationsForEveryone");

		public void Awake()
		{
			DebugLogging = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Debug Logging", false, "Log every config value as it is applied to the game so you can confirm changes take effect");
			CommandManager.Instance.AddConsoleCommand((ConsoleCommand)(object)new RefreshCommand());
			_harmony.PatchAll(Assembly.GetExecutingAssembly());
			Debug("ConfigurationsForEveryone 1.0.0 loaded, waiting for the main menu to discover content");
		}

		internal static void Debug(string message)
		{
			if (DebugLogging != null && DebugLogging.Value)
			{
				Log.LogInfo((object)message);
			}
		}
	}
	internal sealed class DiscoveredMod
	{
		public string Guid = "";

		public string Name = "";

		public string Version = "";

		public readonly List<CustomItem> Items = new List<CustomItem>();

		public readonly List<CustomPiece> Pieces = new List<CustomPiece>();

		public readonly List<CustomCreature> Creatures = new List<CustomCreature>();

		public int Count => Items.Count + Pieces.Count + Creatures.Count;
	}
	internal static class Discovery
	{
		private const BindingFlags InstanceNonPublic = BindingFlags.Instance | BindingFlags.NonPublic;

		public static int LastItemCount { get; private set; }

		public static int LastPieceCount { get; private set; }

		public static int LastCreatureCount { get; private set; }

		public static int LastUnattributed { get; private set; }

		public static List<DiscoveredMod> Run()
		{
			Dictionary<string, DiscoveredMod> dictionary = new Dictionary<string, DiscoveredMod>(StringComparer.OrdinalIgnoreCase);
			int num = 0;
			int num2 = 0;
			int num3 = 0;
			int num4 = 0;
			foreach (CustomItem item in ReadRegistry<CustomItem>(typeof(ItemManager), ItemManager.Instance, "Items"))
			{
				num++;
				DiscoveredMod mod = GetMod(dictionary, ((CustomEntity)item).SourceMod);
				if (mod == null)
				{
					num4++;
				}
				else
				{
					mod.Items.Add(item);
				}
			}
			foreach (CustomPiece item2 in ReadRegistry<CustomPiece>(typeof(PieceManager), PieceManager.Instance, "Pieces"))
			{
				num2++;
				DiscoveredMod mod2 = GetMod(dictionary, ((CustomEntity)item2).SourceMod);
				if (mod2 == null)
				{
					num4++;
				}
				else
				{
					mod2.Pieces.Add(item2);
				}
			}
			foreach (CustomCreature item3 in ReadRegistry<CustomCreature>(typeof(CreatureManager), CreatureManager.Instance, "Creatures"))
			{
				num3++;
				DiscoveredMod mod3 = GetMod(dictionary, ((CustomEntity)item3).SourceMod);
				if (mod3 == null)
				{
					num4++;
				}
				else
				{
					mod3.Creatures.Add(item3);
				}
			}
			LastItemCount = num;
			LastPieceCount = num2;
			LastCreatureCount = num3;
			LastUnattributed = num4;
			if (num4 > 0)
			{
				ConfigurationsForEveryonePlugin.Log.LogWarning((object)$"{num4} registered entries had no resolvable source mod and were skipped");
			}
			return (from m in dictionary.Values
				where m.Count > 0
				orderby m.Name
				select m).ToList();
		}

		private static DiscoveredMod? GetMod(Dictionary<string, DiscoveredMod> byGuid, BepInPlugin? sourceMod)
		{
			if (sourceMod == null || string.IsNullOrEmpty(sourceMod.GUID))
			{
				return null;
			}
			if (!byGuid.TryGetValue(sourceMod.GUID, out DiscoveredMod value))
			{
				value = new DiscoveredMod
				{
					Guid = sourceMod.GUID,
					Name = (string.IsNullOrEmpty(sourceMod.Name) ? sourceMod.GUID : sourceMod.Name),
					Version = (sourceMod.Version?.ToString() ?? "0.0.0")
				};
				byGuid[sourceMod.GUID] = value;
			}
			return value;
		}

		private static IEnumerable<T> ReadRegistry<T>(Type managerType, object instance, string fieldName) where T : class
		{
			FieldInfo field = managerType.GetField(fieldName, BindingFlags.Instance | BindingFlags.NonPublic);
			if (field == null)
			{
				ConfigurationsForEveryonePlugin.Log.LogWarning((object)("Could not find " + managerType.Name + "." + fieldName + " - Jotunn version may have changed"));
				yield break;
			}
			object value = field.GetValue(instance);
			if (!(value is IEnumerable registry))
			{
				yield break;
			}
			DictionaryEntry dictionaryEntry = default(DictionaryEntry);
			foreach (object entry in registry)
			{
				object value2 = entry;
				int num;
				if (entry is DictionaryEntry)
				{
					dictionaryEntry = (DictionaryEntry)entry;
					num = 1;
				}
				else
				{
					num = 0;
				}
				if (num != 0)
				{
					value2 = dictionaryEntry.Value;
				}
				else
				{
					PropertyInfo valueProperty = entry.GetType().GetProperty("Value");
					if (valueProperty != null && entry.GetType().IsGenericType)
					{
						value2 = valueProperty.GetValue(entry);
					}
				}
				if (value2 is T typed)
				{
					yield return typed;
				}
				dictionaryEntry = default(DictionaryEntry);
			}
		}
	}
	internal sealed class ModConfig
	{
		public readonly ConfigFile File;

		private readonly BepInPlugin _metadata;

		private readonly Dictionary<string, string> _displayNames = new Dictionary<string, string>(StringComparer.Ordinal);

		public ModConfig(DiscoveredMod mod)
		{
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Expected O, but got Unknown
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: Expected O, but got Unknown
			string text = Path.Combine(Paths.ConfigPath, "ConfigurationsForEveryone");
			Directory.CreateDirectory(text);
			string path = Sanitize(mod.Guid) + ".cfg";
			_metadata = new BepInPlugin(mod.Guid, mod.Name, mod.Version);
			File = new ConfigFile(Path.Combine(text, path), true, _metadata)
			{
				SaveOnConfigSet = false
			};
			File.SettingChanged += OnSettingChanged;
			RegisterForSync();
		}

		private void OnSettingChanged(object sender, SettingChangedEventArgs args)
		{
			ConfigEntryBase changedSetting = args.ChangedSetting;
			ConfigurationsForEveryonePlugin.Debug($"[{_metadata.Name}] {changedSetting.Definition.Section} > {changedSetting.Definition.Key} changed to '{changedSetting.BoxedValue}', applied and synced");
		}

		public void SetDisplayName(string section, string displayName)
		{
			if (!string.IsNullOrEmpty(displayName) && displayName != section)
			{
				_displayNames[section] = displayName;
			}
		}

		public ConfigEntry<T> Bind<T>(string section, string key, T value, string description, AcceptableValueBase? acceptableValues = null, int order = 0, Action<ConfigEntryBase>? customDrawer = null)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Expected O, but got Unknown
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Expected O, but got Unknown
			ConfigurationManagerAttributes val = new ConfigurationManagerAttributes
			{
				IsAdminOnly = true,
				Order = order
			};
			if (_displayNames.TryGetValue(section, out string value2))
			{
				val.Category = value2 + " (" + section + ")";
			}
			if (customDrawer != null)
			{
				val.CustomDrawer = customDrawer;
			}
			return File.Bind<T>(section, key, value, new ConfigDescription(description, acceptableValues, new object[1] { val }));
		}

		public void Save()
		{
			File.SaveOnConfigSet = true;
			File.Save();
		}

		private void RegisterForSync()
		{
			try
			{
				SynchronizationManager.Instance.RegisterCustomConfig(File);
			}
			catch (Exception ex)
			{
				ConfigurationsForEveryonePlugin.Log.LogWarning((object)("Could not register " + _metadata.Name + " config for server sync: " + ex.Message));
			}
		}

		public void CreateMenuProxy(GameObject host)
		{
			try
			{
				ProxyPlugin proxyPlugin = host.AddComponent<ProxyPlugin>();
				OverwriteBackingField(typeof(BaseUnityPlugin), proxyPlugin, "Config", File);
				PluginInfo info = ((BaseUnityPlugin)proxyPlugin).Info;
				if (info != null)
				{
					OverwriteProperty(typeof(PluginInfo), info, "Metadata", _metadata);
				}
			}
			catch (Exception ex)
			{
				ConfigurationsForEveryonePlugin.Log.LogWarning((object)("Could not surface " + _metadata.Name + " in the configuration manager: " + ex.Message));
			}
		}

		private static void OverwriteBackingField(Type owner, object instance, string propertyName, object value)
		{
			owner.GetField("<" + propertyName + ">k__BackingField", BindingFlags.Instance | BindingFlags.NonPublic)?.SetValue(instance, value);
		}

		private static void OverwriteProperty(Type owner, object instance, string propertyName, object value)
		{
			MethodInfo methodInfo = owner.GetProperty(propertyName, BindingFlags.Instance | BindingFlags.Public)?.GetSetMethod(nonPublic: true);
			if (methodInfo != null)
			{
				methodInfo.Invoke(instance, new object[1] { value });
			}
			else
			{
				OverwriteBackingField(owner, instance, propertyName, value);
			}
		}

		private static string Sanitize(string name)
		{
			return Path.GetInvalidFileNameChars().Aggregate(name, (string current, char invalid) => current.Replace(invalid, '_'));
		}
	}
	[BepInPlugin("IAmOnTheInternetAndItIsScary.ConfigurationsForEveryone.Proxy", "ConfigurationsForEveryone Proxy", "1.0.0")]
	[BepInProcess("ConfigurationsForEveryone.ProxyNeverRuns")]
	internal class ProxyPlugin : BaseUnityPlugin
	{
	}
	internal static class ConfigBuilder
	{
		private static readonly HashSet<string> Configured = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

		private static GameObject? _proxyHost;

		public static void BuildAll(string trigger)
		{
			try
			{
				List<DiscoveredMod> list = Discovery.Run();
				if (list.Count == 0)
				{
					ConfigurationsForEveryonePlugin.Log.LogInfo((object)$"{trigger}: no RtD content found in the Jotunn registries yet ({Discovery.LastItemCount} item(s), {Discovery.LastPieceCount} piece(s), {Discovery.LastCreatureCount} creature(s) scanned)");
					return;
				}
				int num = 0;
				foreach (DiscoveredMod item in list)
				{
					if (!Configured.Add(item.Guid))
					{
						continue;
					}
					EnsureProxyHost();
					ModConfig modConfig = new ModConfig(item);
					foreach (CustomCreature creature in item.Creatures)
					{
						try
						{
							Creatures.Bind(modConfig, creature);
						}
						catch (Exception ex)
						{
							ConfigurationsForEveryonePlugin.Log.LogWarning((object)(item.Name + ": skipped a creature (" + ex.Message + ")"));
						}
					}
					foreach (CustomItem item2 in item.Items)
					{
						try
						{
							Items.Bind(modConfig, item2);
						}
						catch (Exception ex2)
						{
							ConfigurationsForEveryonePlugin.Log.LogWarning((object)(item.Name + ": skipped an item (" + ex2.Message + ")"));
						}
					}
					foreach (CustomPiece piece in item.Pieces)
					{
						try
						{
							Pieces.Bind(modConfig, piece);
						}
						catch (Exception ex3)
						{
							ConfigurationsForEveryonePlugin.Log.LogWarning((object)(item.Name + ": skipped a piece (" + ex3.Message + ")"));
						}
					}
					modConfig.Save();
					modConfig.CreateMenuProxy(_proxyHost);
					num++;
					ConfigurationsForEveryonePlugin.Log.LogInfo((object)$"✓ {item.Name}: {item.Items.Count} item(s), {item.Pieces.Count} piece(s), {item.Creatures.Count} creature(s) configured");
				}
				if (num > 0)
				{
					ConfigurationsForEveryonePlugin.Log.LogInfo((object)string.Format("✓ Generated synced config files for {0} new mod(s) under config/{1}", num, "ConfigurationsForEveryone"));
				}
			}
			catch (Exception arg)
			{
				ConfigurationsForEveryonePlugin.Log.LogError((object)$"Failed to build configs: {arg}");
			}
		}

		private static void EnsureProxyHost()
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Expected O, but got Unknown
			if (!Object.op_Implicit((Object)(object)_proxyHost))
			{
				_proxyHost = new GameObject("ConfigurationsForEveryone_Proxies");
				Object.DontDestroyOnLoad((Object)(object)_proxyHost);
			}
		}
	}
	internal static class Appliers
	{
		public const string KindItem = "item recipe(s)";

		public const string KindPiece = "piece(s)";

		public const string KindCreatureSpawn = "creature spawn(s)";

		public const string KindCreatureTaming = "creature taming";

		public const string KindCreatureDrops = "creature drops";

		private static readonly List<(Action Run, string Kind)> Registered = new List<(Action, string)>();

		public static void Register(Action applier, string kind)
		{
			Registered.Add((applier, kind));
		}

		public static int RunAll()
		{
			int num = 0;
			Dictionary<string, int> dictionary = new Dictionary<string, int>();
			foreach (var (action, text) in Registered)
			{
				try
				{
					action();
					num++;
					dictionary[text] = ((!dictionary.TryGetValue(text, out var value)) ? 1 : (value + 1));
				}
				catch (Exception ex)
				{
					ConfigurationsForEveryonePlugin.Log.LogWarning((object)("Applier failed (" + text + "): " + ex.Message));
				}
			}
			if (num > 0)
			{
				string text2 = string.Join(", ", from c in dictionary
					orderby c.Key
					select $"{c.Value} {c.Key}");
				ConfigurationsForEveryonePlugin.Log.LogInfo((object)("Applied config to loaded content: " + text2));
			}
			return num;
		}
	}
	internal sealed class ApplyScheduler : MonoBehaviour
	{
		private const float DebounceSeconds = 0.4f;

		private static ApplyScheduler? _instance;

		private static readonly Dictionary<Action, float> Pending = new Dictionary<Action, float>();

		private static readonly List<Action> Due = new List<Action>();

		public static void Schedule(Action applier)
		{
			EnsureInstance();
			Pending[applier] = Time.realtimeSinceStartup + 0.4f;
		}

		private static void EnsureInstance()
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Expected O, but got Unknown
			if (!Object.op_Implicit((Object)(object)_instance))
			{
				GameObject val = new GameObject("ConfigurationsForEveryone_ApplyScheduler");
				Object.DontDestroyOnLoad((Object)(object)val);
				_instance = val.AddComponent<ApplyScheduler>();
			}
		}

		private void Update()
		{
			if (Pending.Count == 0)
			{
				return;
			}
			float realtimeSinceStartup = Time.realtimeSinceStartup;
			Due.Clear();
			foreach (KeyValuePair<Action, float> item in Pending)
			{
				if (realtimeSinceStartup >= item.Value)
				{
					Due.Add(item.Key);
				}
			}
			if (Due.Count == 0)
			{
				return;
			}
			foreach (Action item2 in Due)
			{
				Pending.Remove(item2);
				try
				{
					item2();
				}
				catch (Exception ex)
				{
					ConfigurationsForEveryonePlugin.Log.LogWarning((object)("Applier failed: " + ex.Message));
				}
			}
			ConfigurationsForEveryonePlugin.Debug($"Config edit settled, re-applied {Due.Count} group(s) to prefabs");
		}
	}
	[HarmonyPatch(typeof(ZNetScene), "Awake")]
	internal static class ZNetSceneAwakePatch
	{
		[HarmonyPostfix]
		private static void Postfix()
		{
			ConfigBuilder.BuildAll("ZNetScene.Awake");
			Appliers.RunAll();
		}
	}
	[HarmonyPatch(typeof(ObjectDB), "Awake")]
	internal static class ObjectDBAwakePatch
	{
		[HarmonyPostfix]
		private static void Postfix()
		{
			ConfigBuilder.BuildAll("ObjectDB.Awake");
			Appliers.RunAll();
		}
	}
	internal static class Reqs
	{
		private const string MockPrefix = "JVLmock_";

		public static string Localize(string? token, string fallback)
		{
			if (string.IsNullOrEmpty(token))
			{
				return fallback;
			}
			try
			{
				if (Localization.instance == null)
				{
					return fallback;
				}
				string text = Localization.instance.Localize(token);
				return (string.IsNullOrWhiteSpace(text) || text.StartsWith("[")) ? fallback : text;
			}
			catch
			{
				return fallback;
			}
		}

		public static string CleanName(string? prefabName)
		{
			if (string.IsNullOrEmpty(prefabName))
			{
				return "";
			}
			return prefabName.StartsWith("JVLmock_") ? prefabName.Substring("JVLmock_".Length) : prefabName;
		}

		public static string SerializeRequirements(Requirement[]? requirements, bool includePerLevel = false)
		{
			if (requirements == null)
			{
				return "";
			}
			return string.Join(",", from r in requirements
				where r != null && Object.op_Implicit((Object)(object)r.m_resItem)
				select includePerLevel ? $"{CleanName(((Object)r.m_resItem).name)}:{r.m_amount}:{r.m_amountPerLevel}" : $"{CleanName(((Object)r.m_resItem).name)}:{r.m_amount}");
		}

		public static Requirement[] ParseRequirements(string value)
		{
			if (!Object.op_Implicit((Object)(object)ObjectDB.instance) || string.IsNullOrWhiteSpace(value))
			{
				return Array.Empty<Requirement>();
			}
			return (from requirement in (from part in value.Split(new char[1] { ',' })
					select part.Trim() into part
					where part.Length > 0
					select part).Select((Func<string, Requirement>)delegate(string part)
				{
					//IL_0074: Unknown result type (might be due to invalid IL or missing references)
					//IL_0079: Unknown result type (might be due to invalid IL or missing references)
					//IL_0081: Unknown result type (might be due to invalid IL or missing references)
					//IL_0088: Unknown result type (might be due to invalid IL or missing references)
					//IL_0090: Unknown result type (might be due to invalid IL or missing references)
					string[] array = part.Split(new char[1] { ':' });
					string text = CleanName(array[0].Trim());
					int result;
					int num = ((array.Length <= 1 || !int.TryParse(array[1], out result)) ? 1 : result);
					int result2;
					int amountPerLevel = ((array.Length > 2 && int.TryParse(array[2], out result2)) ? result2 : num);
					GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(text);
					ItemDrop val = ((itemPrefab != null) ? itemPrefab.GetComponent<ItemDrop>() : null);
					return ((Object)(object)val == (Object)null) ? ((Requirement)null) : new Requirement
					{
						m_resItem = val,
						m_amount = num,
						m_amountPerLevel = amountPerLevel,
						m_recover = true
					};
				})
				where requirement != null
				select (requirement)).ToArray();
		}

		public static string SerializeDrops(List<Drop>? drops)
		{
			if (drops == null)
			{
				return "";
			}
			return string.Join(",", from d in drops
				where d != null && Object.op_Implicit((Object)(object)d.m_prefab)
				select $"{CleanName(((Object)d.m_prefab).name)}:{d.m_amountMin}-{d.m_amountMax}:{(d.m_chance * 100f).ToString(CultureInfo.InvariantCulture)}");
		}

		public static List<Drop> ParseDrops(string value)
		{
			//IL_015a: Unknown result type (might be due to invalid IL or missing references)
			//IL_015f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0167: Unknown result type (might be due to invalid IL or missing references)
			//IL_016f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0177: Unknown result type (might be due to invalid IL or missing references)
			//IL_0185: Unknown result type (might be due to invalid IL or missing references)
			//IL_018c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0198: Expected O, but got Unknown
			List<Drop> list = new List<Drop>();
			if (!Object.op_Implicit((Object)(object)ZNetScene.instance) || string.IsNullOrWhiteSpace(value))
			{
				return list;
			}
			foreach (string item in from s in value.Split(new char[1] { ',' })
				select s.Trim() into s
				where s.Length > 0
				select s)
			{
				string[] array = item.Split(new char[1] { ':' });
				GameObject prefab = ZNetScene.instance.GetPrefab(CleanName(array[0].Trim()));
				if (Object.op_Implicit((Object)(object)prefab))
				{
					int result = 1;
					int amountMax = 1;
					if (array.Length > 1)
					{
						string[] array2 = array[1].Split(new char[1] { '-' });
						int.TryParse(array2[0], out result);
						amountMax = ((array2.Length > 1 && int.TryParse(array2[1], out var result2)) ? result2 : result);
					}
					float result3 = 100f;
					if (array.Length > 2)
					{
						float.TryParse(array[2], NumberStyles.Float, CultureInfo.InvariantCulture, out result3);
					}
					list.Add(new Drop
					{
						m_prefab = prefab,
						m_amountMin = result,
						m_amountMax = amountMax,
						m_chance = result3 / 100f,
						m_onePerPlayer = false,
						m_levelMultiplier = true
					});
				}
			}
			return list;
		}
	}
	internal static class Creatures
	{
		private enum SpawnTime
		{
			Day,
			Night,
			Always
		}

		private enum SpawnArea
		{
			Center,
			Edge,
			Everywhere
		}

		private enum ForestSetting
		{
			Yes,
			No,
			Both
		}

		private static readonly FieldRef<BaseAI, Tameable> BaseAiTamableRef = AccessTools.FieldRefAccess<BaseAI, Tameable>("m_tamable");

		public static void Bind(ModConfig config, CustomCreature creature)
		{
			GameObject prefab = creature.Prefab;
			if (!((Object)(object)prefab == (Object)null))
			{
				string name = ((Object)prefab).name;
				Character val = default(Character);
				config.SetDisplayName(name, prefab.TryGetComponent<Character>(ref val) ? Reqs.Localize(val.m_name, name) : name);
				if (creature.Spawns.Count > 0)
				{
					BindSpawn(config, creature, name);
				}
				BindTaming(config, prefab, name);
				BindDrops(config, prefab, name);
			}
		}

		private static void BindSpawn(ModConfig config, CustomCreature creature, string section)
		{
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			SpawnData val = creature.Spawns[0];
			ConfigEntry<bool> enabled = config.Bind(section, "Spawn Enabled", val.m_enabled, "Whether this creature spawns naturally in the world", null, 40);
			ConfigEntry<Biome> biome = config.Bind<Biome>(section, "Spawn Biome", val.m_biome, "Biome the creature spawns in", null, 39);
			ConfigEntry<SpawnArea> area = config.Bind(section, "Spawn Area", FromBiomeArea(val.m_biomeArea), "Where inside the biome the creature spawns", null, 38);
			ConfigEntry<SpawnTime> time = config.Bind(section, "Spawn Time", FromSpawnData(val), "Time of day the creature spawns", null, 37);
			ConfigEntry<int> maximum = config.Bind(section, "Maximum Count", val.m_maxSpawned, "Maximum number of this creature near the player before spawning stops", null, 36);
			ConfigEntry<float> interval = config.Bind(section, "Spawn Interval", val.m_spawnInterval, "Seconds between spawn attempts", null, 35);
			ConfigEntry<float> chance = config.Bind(section, "Spawn Chance", val.m_spawnChance, "Percent chance to spawn on each attempt", (AcceptableValueBase?)(object)new AcceptableValueRange<float>(0f, 100f), 34);
			ConfigEntry<int> groupMin = config.Bind(section, "Group Size Min", val.m_groupSizeMin, "Minimum number spawned together", null, 33);
			ConfigEntry<int> groupMax = config.Bind(section, "Group Size Max", val.m_groupSizeMax, "Maximum number spawned together", null, 32);
			ConfigEntry<float> altitudeMin = config.Bind(section, "Altitude Min", val.m_minAltitude, "Minimum altitude required to spawn", null, 31);
			ConfigEntry<float> altitudeMax = config.Bind(section, "Altitude Max", val.m_maxAltitude, "Maximum altitude allowed to spawn", null, 30);
			ConfigEntry<float> oceanMin = config.Bind(section, "Ocean Depth Min", val.m_minOceanDepth, "Minimum ocean depth required to spawn", null, 29);
			ConfigEntry<float> oceanMax = config.Bind(section, "Ocean Depth Max", val.m_maxOceanDepth, "Maximum ocean depth allowed to spawn", null, 28);
			ConfigEntry<float> groundOffset = config.Bind(section, "Spawn Altitude", val.m_groundOffset, "Height above the ground the creature spawns at", null, 27);
			ConfigEntry<ForestSetting> forest = config.Bind(section, "Forest Condition", FromForest(val), "Whether the creature spawns in forests, outside forests, or both", null, 26);
			ConfigEntry<bool> huntPlayer = config.Bind(section, "Hunt Player", val.m_huntPlayer, "Whether the creature immediately hunts the player after spawning", null, 25);
			ConfigEntry<bool> canHaveStars = config.Bind(section, "Can Have Stars", val.m_maxLevel > 1, "Whether the creature can spawn with stars", null, 24);
			ConfigEntry<string> globalKey = config.Bind(section, "Required Global Key", val.m_requiredGlobalKey ?? "", "Global key required before the creature spawns", null, 23);
			ConfigEntry<string> environments = config.Bind(section, "Required Weather", string.Join(",", val.m_requiredEnvironments ?? new List<string>()), "Comma separated weather environment names required to spawn", null, 22);
			Action applySpawn = ApplySpawn;
			applySpawn();
			Appliers.Register(applySpawn, "creature spawn(s)");
			enabled.SettingChanged += delegate
			{
				ApplyScheduler.Schedule(applySpawn);
			};
			biome.SettingChanged += delegate
			{
				ApplyScheduler.Schedule(applySpawn);
			};
			area.SettingChanged += delegate
			{
				ApplyScheduler.Schedule(applySpawn);
			};
			time.SettingChanged += delegate
			{
				ApplyScheduler.Schedule(applySpawn);
			};
			maximum.SettingChanged += delegate
			{
				ApplyScheduler.Schedule(applySpawn);
			};
			interval.SettingChanged += delegate
			{
				ApplyScheduler.Schedule(applySpawn);
			};
			chance.SettingChanged += delegate
			{
				ApplyScheduler.Schedule(applySpawn);
			};
			groupMin.SettingChanged += delegate
			{
				ApplyScheduler.Schedule(applySpawn);
			};
			groupMax.SettingChanged += delegate
			{
				ApplyScheduler.Schedule(applySpawn);
			};
			altitudeMin.SettingChanged += delegate
			{
				ApplyScheduler.Schedule(applySpawn);
			};
			altitudeMax.SettingChanged += delegate
			{
				ApplyScheduler.Schedule(applySpawn);
			};
			oceanMin.SettingChanged += delegate
			{
				ApplyScheduler.Schedule(applySpawn);
			};
			oceanMax.SettingChanged += delegate
			{
				ApplyScheduler.Schedule(applySpawn);
			};
			groundOffset.SettingChanged += delegate
			{
				ApplyScheduler.Schedule(applySpawn);
			};
			forest.SettingChanged += delegate
			{
				ApplyScheduler.Schedule(applySpawn);
			};
			huntPlayer.SettingChanged += delegate
			{
				ApplyScheduler.Schedule(applySpawn);
			};
			canHaveStars.SettingChanged += delegate
			{
				ApplyScheduler.Schedule(applySpawn);
			};
			globalKey.SettingChanged += delegate
			{
				ApplyScheduler.Schedule(applySpawn);
			};
			environments.SettingChanged += delegate
			{
				ApplyScheduler.Schedule(applySpawn);
			};
			void ApplySpawn()
			{
				//IL_0039: Unknown result type (might be due to invalid IL or missing references)
				//IL_003e: Unknown result type (might be due to invalid IL or missing references)
				//IL_004f: Unknown result type (might be due to invalid IL or missing references)
				//IL_0054: Unknown result type (might be due to invalid IL or missing references)
				//IL_02a0: Unknown result type (might be due to invalid IL or missing references)
				foreach (SpawnData spawn in creature.Spawns)
				{
					spawn.m_enabled = enabled.Value;
					spawn.m_biome = biome.Value;
					spawn.m_biomeArea = ToBiomeArea(area.Value);
					spawn.m_maxSpawned = maximum.Value;
					spawn.m_spawnInterval = interval.Value;
					spawn.m_spawnChance = chance.Value;
					spawn.m_groupSizeMin = groupMin.Value;
					spawn.m_groupSizeMax = groupMax.Value;
					spawn.m_minAltitude = altitudeMin.Value;
					spawn.m_maxAltitude = altitudeMax.Value;
					spawn.m_minOceanDepth = oceanMin.Value;
					spawn.m_maxOceanDepth = oceanMax.Value;
					spawn.m_groundOffset = groundOffset.Value;
					spawn.m_huntPlayer = huntPlayer.Value;
					spawn.m_maxLevel = ((!canHaveStars.Value) ? 1 : 3);
					spawn.m_requiredGlobalKey = globalKey.Value;
					spawn.m_requiredEnvironments = (from s in environments.Value.Split(new char[1] { ',' })
						select s.Trim() into s
						where s.Length > 0
						select s).ToList();
					SpawnData val2 = spawn;
					SpawnTime value = time.Value;
					bool spawnAtNight = (uint)(value - 1) <= 1u;
					val2.m_spawnAtNight = spawnAtNight;
					SpawnData val3 = spawn;
					value = time.Value;
					spawnAtNight = ((value == SpawnTime.Day || value == SpawnTime.Always) ? true : false);
					val3.m_spawnAtDay = spawnAtNight;
					SpawnData val4 = spawn;
					ForestSetting value2 = forest.Value;
					spawnAtNight = ((value2 == ForestSetting.Yes || value2 == ForestSetting.Both) ? true : false);
					val4.m_inForest = spawnAtNight;
					SpawnData val5 = spawn;
					value2 = forest.Value;
					spawnAtNight = (uint)(value2 - 1) <= 1u;
					val5.m_outsideForest = spawnAtNight;
				}
				ConfigurationsForEveryonePlugin.Debug($"[{section}] applied spawn: enabled={enabled.Value} biome={biome.Value} chance={chance.Value} max={maximum.Value}");
			}
		}

		private static void BindTaming(ModConfig config, GameObject prefab, string section)
		{
			//IL_00e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a4: Unknown result type (might be due to invalid IL or missing references)
			Tameable val = default(Tameable);
			bool value = prefab.TryGetComponent<Tameable>(ref val);
			float value2 = (Object.op_Implicit((Object)(object)val) ? val.m_fedDuration : 0f);
			float value3 = (Object.op_Implicit((Object)(object)val) ? val.m_tamingTime : 1800f);
			bool value4 = Object.op_Implicit((Object)(object)val) && val.m_startsTamed;
			MonsterAI val2 = default(MonsterAI);
			string value5 = (prefab.TryGetComponent<MonsterAI>(ref val2) ? string.Join(",", from i in val2.m_consumeItems
				where Object.op_Implicit((Object)(object)i) && i.m_itemData != null && Object.op_Implicit((Object)(object)i.m_itemData.m_dropPrefab)
				select Reqs.CleanName(((Object)i.m_itemData.m_dropPrefab).name)) : "");
			Character val3 = default(Character);
			Faction value6 = (Faction)((!prefab.TryGetComponent<Character>(ref val3)) ? 1 : ((int)val3.m_faction));
			ConfigEntry<bool> canBeTamed = config.Bind(section, "Can Be Tamed", value, "Whether the creature can be tamed", null, 19);
			ConfigEntry<string> foodItems = config.Bind(section, "Food Items", value5, "Comma separated item names the creature eats to tame", null, 18);
			ConfigEntry<float> fedDuration = config.Bind(section, "Fed Duration", value2, "Seconds the creature stays fed after eating", null, 17);
			ConfigEntry<float> tamingTime = config.Bind(section, "Taming Time", value3, "Seconds it takes to fully tame the creature", null, 16);
			ConfigEntry<bool> spawnsTamed = config.Bind(section, "Spawns Tamed", value4, "Whether the creature spawns already tamed", null, 15);
			ConfigEntry<Faction> faction = config.Bind<Faction>(section, "Faction", value6, "Faction the creature belongs to", null, 14);
			Action applyTaming = ApplyTaming;
			Appliers.Register(applyTaming, "creature taming");
			canBeTamed.SettingChanged += delegate
			{
				ApplyScheduler.Schedule(applyTaming);
			};
			foodItems.SettingChanged += delegate
			{
				ApplyScheduler.Schedule(applyTaming);
			};
			fedDuration.SettingChanged += delegate
			{
				ApplyScheduler.Schedule(applyTaming);
			};
			tamingTime.SettingChanged += delegate
			{
				ApplyScheduler.Schedule(applyTaming);
			};
			spawnsTamed.SettingChanged += delegate
			{
				ApplyScheduler.Schedule(applyTaming);
			};
			faction.SettingChanged += delegate
			{
				ApplyScheduler.Schedule(applyTaming);
			};
			void ApplyTaming()
			{
				//IL_021b: Unknown result type (might be due to invalid IL or missing references)
				//IL_01e1: Unknown result type (might be due to invalid IL or missing references)
				//IL_01e6: Unknown result type (might be due to invalid IL or missing references)
				Tameable val4 = prefab.GetComponent<Tameable>();
				if (canBeTamed.Value && !Object.op_Implicit((Object)(object)val4))
				{
					val4 = prefab.AddComponent<Tameable>();
					BaseAI val5 = default(BaseAI);
					if (prefab.TryGetComponent<BaseAI>(ref val5))
					{
						BaseAiTamableRef.Invoke(val5) = val4;
					}
				}
				else if (!canBeTamed.Value && Object.op_Implicit((Object)(object)val4))
				{
					Object.Destroy((Object)(object)val4);
					val4 = null;
				}
				if (Object.op_Implicit((Object)(object)val4))
				{
					val4.m_fedDuration = fedDuration.Value;
					val4.m_tamingTime = tamingTime.Value;
					val4.m_startsTamed = spawnsTamed.Value;
				}
				MonsterAI val6 = default(MonsterAI);
				if (prefab.TryGetComponent<MonsterAI>(ref val6) && Object.op_Implicit((Object)(object)ObjectDB.instance))
				{
					val6.m_consumeItems = (from drop in (from s in foodItems.Value.Split(new char[1] { ',' })
							select s.Trim() into s
							where s.Length > 0
							select s).Select(delegate(string name)
						{
							GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(Reqs.CleanName(name));
							return (itemPrefab != null) ? itemPrefab.GetComponent<ItemDrop>() : null;
						})
						where (Object)(object)drop != (Object)null
						select (drop)).ToList();
				}
				Character val7 = default(Character);
				if (prefab.TryGetComponent<Character>(ref val7))
				{
					val7.m_faction = faction.Value;
				}
				ConfigurationsForEveryonePlugin.Debug($"[{section}] applied taming: canBeTamed={canBeTamed.Value} faction={faction.Value} spawnsTamed={spawnsTamed.Value} food='{foodItems.Value}'");
			}
		}

		private static void BindDrops(ModConfig config, GameObject prefab, string section)
		{
			CharacterDrop component = prefab.GetComponent<CharacterDrop>();
			string value = (Object.op_Implicit((Object)(object)component) ? Reqs.SerializeDrops(component.m_drops) : "");
			ConfigEntry<string> drops = config.Bind(section, "Drops", value, "Comma separated itemName:min-max:chance drops when killed", null, 13, Drawer.Drops);
			Action applyDrops = ApplyDrops;
			Appliers.Register(applyDrops, "creature drops");
			drops.SettingChanged += delegate
			{
				ApplyScheduler.Schedule(applyDrops);
			};
			void ApplyDrops()
			{
				if (!Object.op_Implicit((Object)(object)ZNetScene.instance))
				{
					ConfigurationsForEveryonePlugin.Debug("[" + section + "] drops not applied yet: world not loaded");
				}
				else
				{
					List<Drop> list = Reqs.ParseDrops(drops.Value);
					if (list.Count == 0 && drops.Value.Trim().Length > 0)
					{
						ConfigurationsForEveryonePlugin.Log.LogWarning((object)("[" + section + "] drops '" + drops.Value + "' resolved to nothing, leaving existing drops unchanged"));
					}
					else
					{
						CharacterDrop val = prefab.GetComponent<CharacterDrop>();
						if (!Object.op_Implicit((Object)(object)val))
						{
							val = prefab.AddComponent<CharacterDrop>();
						}
						val.m_drops = list;
						ConfigurationsForEveryonePlugin.Debug($"[{section}] applied {list.Count} drop(s) '{drops.Value}' (visible on next kill)");
					}
				}
			}
		}

		private static SpawnTime FromSpawnData(SpawnData data)
		{
			if (data.m_spawnAtDay && data.m_spawnAtNight)
			{
				return SpawnTime.Always;
			}
			return data.m_spawnAtNight ? SpawnTime.Night : SpawnTime.Day;
		}

		private static SpawnArea FromBiomeArea(BiomeArea area)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Invalid comparison between Unknown and I4
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Invalid comparison between Unknown and I4
			if (1 == 0)
			{
			}
			SpawnArea result = (((int)area == 1) ? SpawnArea.Edge : (((int)area != 2) ? SpawnArea.Everywhere : SpawnArea.Center));
			if (1 == 0)
			{
			}
			return result;
		}

		private static BiomeArea ToBiomeArea(SpawnArea area)
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: 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_0015: 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)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			if (1 == 0)
			{
			}
			BiomeArea result = (BiomeArea)(area switch
			{
				SpawnArea.Center => 2, 
				SpawnArea.Edge => 1, 
				_ => 3, 
			});
			if (1 == 0)
			{
			}
			return result;
		}

		private static ForestSetting FromForest(SpawnData data)
		{
			if (data.m_inForest && data.m_outsideForest)
			{
				return ForestSetting.Both;
			}
			return (!data.m_inForest) ? ForestSetting.No : ForestSetting.Yes;
		}
	}
	internal static class Items
	{
		public static void Bind(ModConfig config, CustomItem item)
		{
			GameObject itemPrefab = item.ItemPrefab;
			if ((Object)(object)itemPrefab == (Object)null)
			{
				return;
			}
			CustomRecipe recipe = item.Recipe;
			Recipe recipe2 = ((recipe != null) ? recipe.Recipe : null);
			string section;
			ConfigEntry<bool> craftable;
			ConfigEntry<int> craftAmount;
			ConfigEntry<string> station;
			ConfigEntry<int> stationLevel;
			ConfigEntry<string> resources;
			if (!((Object)(object)recipe2 == (Object)null))
			{
				section = ((Object)itemPrefab).name;
				ItemDrop component = itemPrefab.GetComponent<ItemDrop>();
				bool flag = Object.op_Implicit((Object)(object)component) && component.m_itemData?.m_shared != null && component.m_itemData.m_shared.m_maxQuality > 1;
				config.SetDisplayName(section, Reqs.Localize((Object.op_Implicit((Object)(object)component) && component.m_itemData?.m_shared != null) ? component.m_itemData.m_shared.m_name : null, section));
				craftable = config.Bind(section, "Craftable", recipe2.m_enabled, "Whether the item can be crafted", null, 40);
				craftAmount = config.Bind(section, "Craft Amount", recipe2.m_amount, "Number of items produced by a single craft", null, 39);
				station = config.Bind(section, "Crafting Station", Object.op_Implicit((Object)(object)recipe2.m_craftingStation) ? Reqs.CleanName(((Object)recipe2.m_craftingStation).name) : "", "Crafting station prefab name required to craft, empty for hand crafting", null, 38);
				stationLevel = config.Bind(section, "Crafting Station Level", recipe2.m_minStationLevel, "Minimum crafting station level required to craft", null, 37);
				resources = config.Bind(section, "Crafting Costs", Reqs.SerializeRequirements(recipe2.m_resources, flag), flag ? "Comma separated itemName:amount:perUpgradeLevel resources, the third number is added for each upgrade level" : "Comma separated itemName:amount resources required to craft", null, 36, flag ? Drawer.RequirementsWithUpgrade : Drawer.Requirements);
				Action apply = Apply;
				Appliers.Register(apply, "item recipe(s)");
				craftable.SettingChanged += delegate
				{
					ApplyScheduler.Schedule(apply);
				};
				craftAmount.SettingChanged += delegate
				{
					ApplyScheduler.Schedule(apply);
				};
				station.SettingChanged += delegate
				{
					ApplyScheduler.Schedule(apply);
				};
				stationLevel.SettingChanged += delegate
				{
					ApplyScheduler.Schedule(apply);
				};
				resources.SettingChanged += delegate
				{
					ApplyScheduler.Schedule(apply);
				};
			}
			void Apply()
			{
				recipe2.m_enabled = craftable.Value;
				recipe2.m_amount = craftAmount.Value;
				recipe2.m_minStationLevel = stationLevel.Value;
				if (Object.op_Implicit((Object)(object)ObjectDB.instance))
				{
					Requirement[] array = Reqs.ParseRequirements(resources.Value);
					if (array.Length != 0)
					{
						recipe2.m_resources = array;
					}
				}
				if (Object.op_Implicit((Object)(object)ZNetScene.instance))
				{
					if (string.IsNullOrEmpty(station.Value))
					{
						recipe2.m_craftingStation = null;
					}
					else
					{
						GameObject prefab = ZNetScene.instance.GetPrefab(Reqs.CleanName(station.Value));
						CraftingStation val = ((prefab != null) ? prefab.GetComponent<CraftingStation>() : null);
						if ((Object)(object)val == (Object)null)
						{
							ConfigurationsForEveryonePlugin.Log.LogWarning((object)("[" + section + "] crafting station '" + station.Value + "' could not be resolved"));
						}
						recipe2.m_craftingStation = val;
					}
				}
				ConfigurationsForEveryonePlugin.Debug($"[{section}] applied item: craftable={craftable.Value} amount={craftAmount.Value} station='{station.Value}' level={stationLevel.Value} costs='{resources.Value}'");
			}
		}
	}
	internal static class Pieces
	{
		public static void Bind(ModConfig config, CustomPiece customPiece)
		{
			//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
			Piece piece = customPiece.Piece;
			string section;
			ConfigEntry<bool> buildable;
			ConfigEntry<PieceCategory> category;
			ConfigEntry<string> station;
			ConfigEntry<string> resources;
			if (!((Object)(object)piece == (Object)null))
			{
				section = (Object.op_Implicit((Object)(object)customPiece.PiecePrefab) ? ((Object)customPiece.PiecePrefab).name : ((Object)piece).name);
				config.SetDisplayName(section, Reqs.Localize(piece.m_name, section));
				buildable = config.Bind(section, "Buildable", piece.m_enabled, "Whether the piece can be built", null, 40);
				category = config.Bind<PieceCategory>(section, "Category", piece.m_category, "Build menu category the piece appears in", null, 39);
				station = config.Bind(section, "Crafting Station", Object.op_Implicit((Object)(object)piece.m_craftingStation) ? Reqs.CleanName(((Object)piece.m_craftingStation).name) : "", "Crafting station prefab name required to build, empty for none", null, 38);
				resources = config.Bind(section, "Build Costs", Reqs.SerializeRequirements(piece.m_resources), "Comma separated itemName:amount resources required to build", null, 37, Drawer.Requirements);
				Action apply = Apply;
				Appliers.Register(apply, "piece(s)");
				buildable.SettingChanged += delegate
				{
					ApplyScheduler.Schedule(apply);
				};
				category.SettingChanged += delegate
				{
					ApplyScheduler.Schedule(apply);
				};
				station.SettingChanged += delegate
				{
					ApplyScheduler.Schedule(apply);
				};
				resources.SettingChanged += delegate
				{
					ApplyScheduler.Schedule(apply);
				};
			}
			void Apply()
			{
				//IL_0023: 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_015c: Unknown result type (might be due to invalid IL or missing references)
				piece.m_enabled = buildable.Value;
				piece.m_category = category.Value;
				if (Object.op_Implicit((Object)(object)ObjectDB.instance))
				{
					Requirement[] array = Reqs.ParseRequirements(resources.Value);
					if (array.Length != 0)
					{
						piece.m_resources = array;
					}
				}
				if (Object.op_Implicit((Object)(object)ZNetScene.instance))
				{
					if (string.IsNullOrEmpty(station.Value))
					{
						piece.m_craftingStation = null;
					}
					else
					{
						GameObject prefab = ZNetScene.instance.GetPrefab(Reqs.CleanName(station.Value));
						CraftingStation val = ((prefab != null) ? prefab.GetComponent<CraftingStation>() : null);
						if ((Object)(object)val == (Object)null)
						{
							ConfigurationsForEveryonePlugin.Log.LogWarning((object)("[" + section + "] crafting station '" + station.Value + "' could not be resolved"));
						}
						piece.m_craftingStation = val;
					}
				}
				ConfigurationsForEveryonePlugin.Debug($"[{section}] applied piece: buildable={buildable.Value} category={category.Value} station='{station.Value}' costs='{resources.Value}'");
			}
		}
	}
	internal static class Drawer
	{
		private struct Req
		{
			public string Name;

			public int Amount;

			public int PerLevel;
		}

		private struct DropRow
		{
			public string Name;

			public int Min;

			public int Max;

			public float Chance;
		}

		public static readonly Action<ConfigEntryBase> Requirements = delegate(ConfigEntryBase cfg)
		{
			Guard(cfg, delegate(ConfigEntryBase c)
			{
				DrawRequirements(c, withUpgrade: false);
			});
		};

		public static readonly Action<ConfigEntryBase> RequirementsWithUpgrade = delegate(ConfigEntryBase cfg)
		{
			Guard(cfg, delegate(ConfigEntryBase c)
			{
				DrawRequirements(c, withUpgrade: true);
			});
		};

		public static readonly Action<ConfigEntryBase> Drops = delegate(ConfigEntryBase cfg)
		{
			Guard(cfg, DrawDrops);
		};

		private const int ButtonWidth = 24;

		private static GUIStyle? _textStyle;

		private static GUIStyle? _buttonStyle;

		private static GUIStyle? _centerLabelStyle;

		private static object? _configManager;

		private static bool _configManagerResolved;

		private static void EnsureStyles()
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Expected O, but got Unknown
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Expected O, but got Unknown
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Expected O, but got Unknown
			if (_textStyle == null)
			{
				_textStyle = new GUIStyle(GUI.skin.textField);
				_buttonStyle = new GUIStyle(GUI.skin.button);
				_centerLabelStyle = new GUIStyle(GUI.skin.label)
				{
					alignment = (TextAnchor)4
				};
			}
		}

		private static void Guard(ConfigEntryBase cfg, Action<ConfigEntryBase> draw)
		{
			try
			{
				EnsureStyles();
				draw(cfg);
			}
			catch (Exception ex)
			{
				GUILayout.Label((string)cfg.BoxedValue, Array.Empty<GUILayoutOption>());
				ConfigurationsForEveryonePlugin.Debug("drawer failed for '" + cfg.Definition.Key + "': " + ex.Message);
			}
		}

		private static int ColumnWidth()
		{
			if (!_configManagerResolved)
			{
				_configManagerResolved = true;
				try
				{
					Type type = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault((Assembly a) => a.GetName().Name == "ConfigurationManager")?.GetType("ConfigurationManager.ConfigurationManager");
					if (type != null && Object.op_Implicit((Object)(object)Chainloader.ManagerObject))
					{
						_configManager = Chainloader.ManagerObject.GetComponent(type);
					}
				}
				catch
				{
				}
			}
			try
			{
				if (_configManager?.GetType().GetProperty("RightColumnWidth", BindingFlags.Instance | BindingFlags.NonPublic)?.GetValue(_configManager) is int num && num > 0)
				{
					return num;
				}
			}
			catch
			{
			}
			return 130;
		}

		private static void DrawRequirements(ConfigEntryBase cfg, bool withUpgrade)
		{
			List<Req> list = ParseReqs((string)cfg.BoxedValue);
			List<Req> list2 = new List<Req>();
			bool flag = false;
			int num = 119 + (withUpgrade ? 77 : 0);
			int num2 = Math.Max(60, ColumnWidth() - num);
			GUILayout.BeginVertical(Array.Empty<GUILayoutOption>());
			if (list.Count == 0)
			{
				list.Add(new Req
				{
					Name = "",
					Amount = 1,
					PerLevel = 1
				});
				flag = true;
			}
			foreach (Req item in list)
			{
				GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
				int num3 = item.Amount;
				if (int.TryParse(GUILayout.TextField(num3.ToString(), _textStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(45f) }), out var result) && result != num3)
				{
					num3 = result;
					flag = true;
				}
				GUILayout.Label("x", _centerLabelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(12f) });
				string text = GUILayout.TextField(item.Name, _textStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width((float)num2) });
				flag = flag || text != item.Name;
				int num4 = item.PerLevel;
				if (withUpgrade)
				{
					if (int.TryParse(GUILayout.TextField(num4.ToString(), _textStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(45f) }), out var result2) && result2 != num4)
					{
						num4 = result2;
						flag = true;
					}
					GUILayout.Label("/lvl", _centerLabelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(32f) });
				}
				bool flag2 = GUILayout.Button("✕", _buttonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(24f) });
				bool flag3 = GUILayout.Button("+", _buttonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(24f) });
				GUILayout.EndHorizontal();
				if (flag2)
				{
					flag = true;
				}
				else
				{
					list2.Add(new Req
					{
						Name = text,
						Amount = num3,
						PerLevel = num4
					});
				}
				if (flag3)
				{
					flag = true;
					list2.Add(new Req
					{
						Name = "",
						Amount = 1,
						PerLevel = 1
					});
				}
			}
			GUILayout.EndVertical();
			if (flag)
			{
				cfg.BoxedValue = SerializeReqs(list2, withUpgrade);
			}
		}

		private static string SerializeReqs(List<Req> reqs, bool withUpgrade)
		{
			return string.Join(",", reqs.Select((Req r) => withUpgrade ? $"{r.Name.Trim()}:{r.Amount}:{r.PerLevel}" : $"{r.Name.Trim()}:{r.Amount}"));
		}

		private static List<Req> ParseReqs(string value)
		{
			List<Req> list = new List<Req>();
			if (string.IsNullOrWhiteSpace(value))
			{
				return list;
			}
			foreach (string item in from p in value.Split(new char[1] { ',' })
				select p.Trim() into p
				where p.Length > 0
				select p)
			{
				string[] array = item.Split(new char[1] { ':' });
				int result;
				int num = ((array.Length <= 1 || !int.TryParse(array[1], out result)) ? 1 : result);
				int result2;
				int perLevel = ((array.Length > 2 && int.TryParse(array[2], out result2)) ? result2 : num);
				list.Add(new Req
				{
					Name = array[0].Trim(),
					Amount = num,
					PerLevel = perLevel
				});
			}
			return list;
		}

		private static void DrawDrops(ConfigEntryBase cfg)
		{
			List<DropRow> list = ParseDrops((string)cfg.BoxedValue);
			List<DropRow> list2 = new List<DropRow>();
			bool flag = false;
			int num = 201;
			int num2 = Math.Max(60, ColumnWidth() - num);
			GUILayout.BeginVertical(Array.Empty<GUILayoutOption>());
			if (list.Count == 0)
			{
				list.Add(new DropRow
				{
					Name = "",
					Min = 1,
					Max = 1,
					Chance = 100f
				});
				flag = true;
			}
			foreach (DropRow item in list)
			{
				GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
				int num3 = item.Min;
				if (int.TryParse(GUILayout.TextField(num3.ToString(), _textStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(35f) }), out var result) && result != num3)
				{
					num3 = result;
					flag = true;
				}
				GUILayout.Label("-", _centerLabelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(10f) });
				int num4 = item.Max;
				if (int.TryParse(GUILayout.TextField(num4.ToString(), _textStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(35f) }), out var result2) && result2 != num4)
				{
					num4 = result2;
					flag = true;
				}
				string text = GUILayout.TextField(item.Name, _textStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width((float)num2) });
				flag = flag || text != item.Name;
				float num5 = item.Chance;
				if (float.TryParse(GUILayout.TextField(num5.ToString(CultureInfo.InvariantCulture), _textStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(45f) }), out var result3) && Math.Abs(result3 - num5) > 1E-05f)
				{
					num5 = result3;
					flag = true;
				}
				GUILayout.Label("%", _centerLabelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(14f) });
				bool flag2 = GUILayout.Button("✕", _buttonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(24f) });
				bool flag3 = GUILayout.Button("+", _buttonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(24f) });
				GUILayout.EndHorizontal();
				if (flag2)
				{
					flag = true;
				}
				else
				{
					list2.Add(new DropRow
					{
						Name = text,
						Min = num3,
						Max = num4,
						Chance = num5
					});
				}
				if (flag3)
				{
					flag = true;
					list2.Add(new DropRow
					{
						Name = "",
						Min = 1,
						Max = 1,
						Chance = 100f
					});
				}
			}
			GUILayout.EndVertical();
			if (flag)
			{
				cfg.BoxedValue = SerializeDrops(list2);
			}
		}

		private static string SerializeDrops(List<DropRow> drops)
		{
			return string.Join(",", drops.Select((DropRow d) => $"{d.Name.Trim()}:{d.Min}-{d.Max}:{d.Chance.ToString(CultureInfo.InvariantCulture)}"));
		}

		private static List<DropRow> ParseDrops(string value)
		{
			List<DropRow> list = new List<DropRow>();
			if (string.IsNullOrWhiteSpace(value))
			{
				return list;
			}
			foreach (string item in from s in value.Split(new char[1] { ',' })
				select s.Trim() into s
				where s.Length > 0
				select s)
			{
				string[] array = item.Split(new char[1] { ':' });
				int result = 1;
				int max = 1;
				if (array.Length > 1)
				{
					string[] array2 = array[1].Split(new char[1] { '-' });
					int.TryParse(array2[0], out result);
					max = ((array2.Length > 1 && int.TryParse(array2[1], out var result2)) ? result2 : result);
				}
				float result3 = 100f;
				if (array.Length > 2)
				{
					float.TryParse(array[2], NumberStyles.Float, CultureInfo.InvariantCulture, out result3);
				}
				list.Add(new DropRow
				{
					Name = array[0].Trim(),
					Min = result,
					Max = max,
					Chance = result3
				});
			}
			return list;
		}
	}
	internal sealed class RefreshCommand : ConsoleCommand
	{
		public override string Name => "cfe_refresh";

		public override string Help => "Re-apply every ConfigurationsForEveryone setting to the loaded game content";

		public override void Run(string[] args, Terminal context)
		{
			int num = Appliers.RunAll();
			if (context != null)
			{
				context.AddString($"ConfigurationsForEveryone re-applied {num} setting group(s)");
			}
		}
	}
}