Decompiled source of CommunityPatchExtras v0.5.2

plugins/CommunityPatchExtras.dll

Decompiled a day ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security.Permissions;
using System.Text;
using System.Text.RegularExpressions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using CommunityPatchExtras.Common;
using CommunityPatchExtras.Patches;
using HarmonyLib;
using Jotunn;
using Jotunn.Configs;
using Jotunn.Entities;
using Jotunn.Managers;
using Jotunn.Utils;
using SimpleJson;
using TMPro;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.UI;
using YamlDotNet.Core;
using YamlDotNet.Core.Events;
using YamlDotNet.Serialization;
using YamlDotNet.Serialization.NamingConventions;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("CommunityPatchExtras")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("CommunityPatchExtras")]
[assembly: AssemblyCopyright("Copyright ©  2021")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("e3243d22-4307-4008-ba36-9f326008cde5")]
[assembly: AssemblyFileVersion("0.5.2")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.5.2.0")]
namespace CommunityPatchExtras
{
	internal static class ConfigFileWatcher
	{
		private class WatchEntry
		{
			internal DateTime LastWriteUTC;

			internal long FileLength;

			internal Action<string> Callback;
		}

		internal class ConfigFileWatcherBehaviour : MonoBehaviour
		{
			private float nextPollTime;

			public void Update()
			{
				if (!(Time.unscaledTime < nextPollTime))
				{
					nextPollTime = Time.unscaledTime + PollInterval();
					Poll();
				}
			}

			private static float PollInterval()
			{
				if (ValConfig.ConfigPollIntervalSeconds == null)
				{
					return 30f;
				}
				return ValConfig.ConfigPollIntervalSeconds.Value;
			}

			private static void Poll()
			{
				if (WatchedFiles.Count == 0)
				{
					return;
				}
				string[] array = WatchedFiles.Keys.ToArray();
				foreach (string text in array)
				{
					if (!WatchedFiles.TryGetValue(text, out var value) || !File.Exists(text))
					{
						continue;
					}
					FileInfo fileInfo = new FileInfo(text);
					DateTime lastWriteTimeUtc = fileInfo.LastWriteTimeUtc;
					long length = fileInfo.Length;
					if (!(lastWriteTimeUtc == value.LastWriteUTC) || length != value.FileLength)
					{
						value.LastWriteUTC = lastWriteTimeUtc;
						value.FileLength = length;
						try
						{
							value.Callback?.Invoke(text);
						}
						catch (Exception ex)
						{
							Logger.LogWarning("ConfigFileWatcher callback for " + text + " threw: " + ex.Message);
						}
					}
				}
			}
		}

		private const float FallbackPollSeconds = 30f;

		private static readonly Dictionary<string, WatchEntry> WatchedFiles = new Dictionary<string, WatchEntry>(StringComparer.OrdinalIgnoreCase);

		private static ConfigFileWatcherBehaviour watchProcess;

		internal static void Initialize()
		{
			//IL_0013: 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)
			//IL_001e: Expected O, but got Unknown
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)watchProcess != (Object)null))
			{
				GameObject val = new GameObject("ValheimCommunityPatchExtras_ConfigFileWatcher");
				Object.DontDestroyOnLoad((Object)val);
				((Object)val).hideFlags = (HideFlags)61;
				watchProcess = val.AddComponent<ConfigFileWatcherBehaviour>();
				Logger.LogDebug("ConfigFileWatcher initialized.");
			}
		}

		internal static void Register(string fullPath, Action<string> onChanged)
		{
			if (!string.IsNullOrEmpty(fullPath))
			{
				DateTime lastWriteUTC = DateTime.MinValue;
				long fileLength = 0L;
				if (File.Exists(fullPath))
				{
					FileInfo fileInfo = new FileInfo(fullPath);
					lastWriteUTC = fileInfo.LastWriteTimeUtc;
					fileLength = fileInfo.Length;
				}
				WatchedFiles[fullPath] = new WatchEntry
				{
					LastWriteUTC = lastWriteUTC,
					FileLength = fileLength,
					Callback = onChanged
				};
				Logger.LogDebug("ConfigFileWatcher watching " + fullPath);
			}
		}

		internal static void RefreshStamp(string fullPath)
		{
			if (string.IsNullOrEmpty(fullPath) || !WatchedFiles.TryGetValue(fullPath, out var value))
			{
				return;
			}
			try
			{
				FileInfo fileInfo = new FileInfo(fullPath);
				value.LastWriteUTC = fileInfo.LastWriteTimeUtc;
				value.FileLength = fileInfo.Length;
			}
			catch (Exception)
			{
				value.LastWriteUTC = DateTime.MinValue;
				value.FileLength = 0L;
			}
		}
	}
	internal static class ConfigNetwork
	{
		private static bool initialized;

		private static Harmony harmony;

		private static readonly HashSet<string> usedRpcNames = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

		private const byte EditProtocolVersion = 1;

		internal static bool ServerConfigsSynced { get; private set; }

		internal static event Action<YamlConfigFile, bool, string> EditResult;

		internal static void Init()
		{
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Expected O, but got Unknown
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Expected O, but got Unknown
			if (initialized)
			{
				return;
			}
			initialized = true;
			SynchronizationManager.OnConfigurationSynchronized += OnConfigurationSynchronized;
			try
			{
				harmony = new Harmony("MidnightsFX.ValheimCommunityPatchExtras.config");
				harmony.Patch((MethodBase)AccessTools.Method(typeof(ZNet), "Shutdown", (Type[])null, (Type[])null), new HarmonyMethod(typeof(ConfigNetwork), "ResetOnWorldUnload", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			}
			catch (Exception ex)
			{
				Logger.LogWarning("Could not patch ZNet.Shutdown for config sync teardown: " + ex.Message);
			}
		}

		internal static void RegisterFile(YamlConfigFile file)
		{
			//IL_00d4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ea: Expected O, but got Unknown
			//IL_00ea: Expected O, but got Unknown
			//IL_018f: 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_01a5: Expected O, but got Unknown
			//IL_01a5: Expected O, but got Unknown
			if (file == null || file.Sync == ConfigSyncMode.LocalOnly)
			{
				return;
			}
			if (string.IsNullOrEmpty(file.RpcName))
			{
				file.RpcName = "ValheimCommunityPatchExtras_" + Path.GetFileNameWithoutExtension(file.FileName);
			}
			if (!usedRpcNames.Add(file.RpcName))
			{
				Logger.LogError("Config RPC name '" + file.RpcName + "' is already in use; " + file.FileName + " will not be synced. Give it an explicit RpcName.");
				return;
			}
			file.Rpc = NetworkManager.Instance.AddRPC(file.RpcName, (CoroutineHandler)((long sender, ZPackage package) => OnServerReceive(file, sender, package)), (CoroutineHandler)((long sender, ZPackage package) => OnClientReceive(file, sender, package)));
			SynchronizationManager.Instance.AddInitialSynchronization(file.Rpc, (Func<ZPackage>)(() => SendFileAsZPackage(file)));
			if (!file.AllowAdminEdit)
			{
				return;
			}
			string text = file.RpcName + "_Edit";
			if (!usedRpcNames.Add(text))
			{
				Logger.LogError("Config RPC name '" + text + "' is already in use; " + file.FileName + " will not accept admin edits.");
			}
			else
			{
				file.EditRpc = NetworkManager.Instance.AddRPC(text, (CoroutineHandler)((long sender, ZPackage package) => OnServerReceiveEdit(file, sender, package)), (CoroutineHandler)((long sender, ZPackage package) => OnClientReceiveEditResult(file, sender, package)));
			}
		}

		internal static bool RequestEdit(YamlConfigFile file, string yaml, out string refusal)
		{
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Expected O, but got Unknown
			refusal = "";
			if (file == null || file.EditRpc == null)
			{
				refusal = "this config cannot be edited remotely.";
				return false;
			}
			if ((Object)(object)ZNet.instance == (Object)null || ZNet.instance.IsServer())
			{
				refusal = "not connected to a server as a client.";
				return false;
			}
			if (SynchronizationManager.Instance != null && !SynchronizationManager.Instance.PlayerIsAdmin)
			{
				refusal = "only server admins can change this.";
				return false;
			}
			ZPackage val = new ZPackage();
			val.Write((byte)1);
			val.Write(yaml);
			file.EditRpc.SendPackage(ZRoutedRpc.instance.GetServerPeerID(), val);
			return true;
		}

		private static IEnumerator OnServerReceiveEdit(YamlConfigFile file, long sender, ZPackage package)
		{
			if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer())
			{
				yield break;
			}
			byte b = package.ReadByte();
			if (b != 1)
			{
				SendEditResult(sender, file, accepted: false, $"This server expects edit protocol v{(byte)1}, " + $"the sender used v{b}. Update so both sides match.");
				yield break;
			}
			string yaml = package.ReadString();
			string message;
			if (!SenderIsAdmin(sender))
			{
				Logger.LogWarning($"Rejecting an edit of {file.FileName} from non-admin peer {sender}.");
				SendEditResult(sender, file, accepted: false, "Only server admins can change " + file.FileName + ".");
			}
			else if (!YamlConfigManager.ApplyEdited(file, yaml, out message))
			{
				Logger.LogWarning($"Admin peer {sender} sent a {file.FileName} that was rejected: {message}");
				SendEditResult(sender, file, accepted: false, message);
			}
			else
			{
				Logger.LogInfo($"{file.FileName} was replaced by admin peer {sender}.");
				SendEditResult(sender, file, accepted: true, message);
				yield return null;
			}
		}

		private static IEnumerator OnClientReceiveEditResult(YamlConfigFile file, long sender, ZPackage package)
		{
			if (package.ReadByte() != 1)
			{
				ConfigNetwork.EditResult?.Invoke(file, arg2: false, "The server answered with an edit protocol this build does not understand.");
				yield break;
			}
			bool arg = package.ReadBool();
			string arg2 = package.ReadString();
			ConfigNetwork.EditResult?.Invoke(file, arg, arg2);
			yield return null;
		}

		private static void SendEditResult(long peer, YamlConfigFile file, bool accepted, string message)
		{
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Expected O, but got Unknown
			if (file.EditRpc != null)
			{
				ZPackage val = new ZPackage();
				val.Write((byte)1);
				val.Write(accepted);
				val.Write(message ?? "");
				file.EditRpc.SendPackage(peer, val);
			}
		}

		internal static void Broadcast(YamlConfigFile file)
		{
			if (file != null && file.Rpc != null && !((Object)(object)ZNet.instance == (Object)null) && ZNet.instance.IsServer())
			{
				file.Rpc.SendPackage(ZNet.instance.m_peers, SendFileAsZPackage(file));
			}
		}

		internal static void ResetServerSyncState()
		{
			ServerConfigsSynced = false;
		}

		private static void OnConfigurationSynchronized(object sender, EventArgs e)
		{
			ServerConfigsSynced = true;
		}

		private static void ResetOnWorldUnload()
		{
			ResetServerSyncState();
		}

		private static IEnumerator OnServerReceive(YamlConfigFile file, long sender, ZPackage package)
		{
			Logger.LogDebug($"Peer {sender} sent {file.FileName}; this config is server-authoritative, ignoring.");
			yield break;
		}

		private static IEnumerator OnClientReceive(YamlConfigFile file, long sender, ZPackage package)
		{
			string text = package.ReadString();
			file.LoadFrom(text, ConfigOrigin.ServerSync);
			if (file.ClientWritesToDisk)
			{
				YamlConfigManager.WriteRawToDisk(file, text);
			}
			yield return null;
		}

		private static ZPackage SendFileAsZPackage(YamlConfigFile file)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Expected O, but got Unknown
			ZPackage val = new ZPackage();
			try
			{
				val.Write(File.Exists(file.Path) ? File.ReadAllText(file.Path) : file.SerializeCurrent());
			}
			catch (Exception ex)
			{
				Logger.LogError("Could not read " + file.FileName + " to send to peers: " + ex.Message);
				val.Write("");
			}
			return val;
		}

		internal static bool SenderIsAdmin(long sender)
		{
			ZNet instance = ZNet.instance;
			ZNetPeer val = ((instance != null) ? instance.GetPeer(sender) : null);
			if (val == null || val.m_socket == null)
			{
				return false;
			}
			return ZNet.instance.IsAdmin(val.m_socket.GetHostName());
		}
	}
	internal class ValidationReport
	{
		internal readonly List<string> Warnings = new List<string>();

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

		internal bool HasErrors => Errors.Count > 0;

		internal ValidationReport Warn(string message)
		{
			Warnings.Add(message);
			return this;
		}

		internal ValidationReport Error(string message)
		{
			Errors.Add(message);
			return this;
		}

		internal ValidationReport Absorb(ValidationReport other)
		{
			if (other == null)
			{
				return this;
			}
			Warnings.AddRange(other.Warnings);
			Errors.AddRange(other.Errors);
			return this;
		}
	}
	internal static class ConfigValidation
	{
		internal static float Prefer(float bepInExValue, float yamlValue, float sentinel = 0f)
		{
			if (bepInExValue != sentinel)
			{
				return bepInExValue;
			}
			return yamlValue;
		}

		internal static string SuggestKey(string unknownKey, IEnumerable<string> knownKeys)
		{
			if (string.IsNullOrEmpty(unknownKey) || knownKeys == null)
			{
				return "";
			}
			int num = Math.Max(1, unknownKey.Length / 4);
			string text = null;
			int num2 = int.MaxValue;
			foreach (string knownKey in knownKeys)
			{
				if (!string.IsNullOrEmpty(knownKey))
				{
					int num3 = Distance(unknownKey, knownKey);
					if (num3 < num2)
					{
						num2 = num3;
						text = knownKey;
					}
				}
			}
			if (text == null || num2 > num)
			{
				return "";
			}
			return " Did you mean '" + text + "'?";
		}

		private static int Distance(string a, string b)
		{
			a = a.ToLowerInvariant();
			b = b.ToLowerInvariant();
			if (a == b)
			{
				return 0;
			}
			if (a.Length == 0)
			{
				return b.Length;
			}
			if (b.Length == 0)
			{
				return a.Length;
			}
			int[] array = new int[b.Length + 1];
			int[] array2 = new int[b.Length + 1];
			for (int i = 0; i <= b.Length; i++)
			{
				array[i] = i;
			}
			for (int j = 1; j <= a.Length; j++)
			{
				array2[0] = j;
				for (int k = 1; k <= b.Length; k++)
				{
					int num = ((a[j - 1] != b[k - 1]) ? 1 : 0);
					array2[k] = Math.Min(Math.Min(array2[k - 1] + 1, array[k] + 1), array[k - 1] + num);
				}
				int[] array3 = array;
				array = array2;
				array2 = array3;
			}
			return array[b.Length];
		}
	}
	internal static class YamlConfigManager
	{
		internal static YamlConfigFile<ExampleSettings> ExampleFile;

		internal static YamlConfigFile<Dictionary<string, int>> ExampleSavedDataFile;

		private const string ExampleHeader = "#################################################\n# CommunityPatchExtras - Example settings\n#\n# Entries is a map of <key>: <entry>. The key is the identity used elsewhere in this mod, so\n# renaming one is a breaking change; DisplayName is only a label and is safe to change.\n#\n#   DisplayName  string   Shown to the player.\n#   Multiplier   float    Scales the thing. 1.0 is unchanged. Range 0.1 - 10.\n#   Mode         enum     Off | Add | Multiply\n#   Prefabs      list     Prefab names this entry applies to. Unknown names are warned about\n#                         and skipped, they do not break the file.\n#\n# A typo in a key or an enum value costs you that one setting and logs a warning naming the\n# line; the rest of the file still loads.\n#################################################";

		private static bool initialized;

		private static readonly List<YamlConfigFile> Files = new List<YamlConfigFile>();

		private static readonly Dictionary<string, YamlConfigFile> ByPath = new Dictionary<string, YamlConfigFile>(StringComparer.OrdinalIgnoreCase);

		internal static IEnumerable<YamlConfigFile> All => Files;

		private static void RegisterConfigFiles()
		{
			RegisterExampleConfigs();
		}

		private static void RegisterExampleConfigs()
		{
			ExampleFile = Register(new YamlConfigFile<ExampleSettings>("ExampleSettings.yaml")
			{
				Header = "#################################################\n# CommunityPatchExtras - Example settings\n#\n# Entries is a map of <key>: <entry>. The key is the identity used elsewhere in this mod, so\n# renaming one is a breaking change; DisplayName is only a label and is safe to change.\n#\n#   DisplayName  string   Shown to the player.\n#   Multiplier   float    Scales the thing. 1.0 is unchanged. Range 0.1 - 10.\n#   Mode         enum     Off | Add | Multiply\n#   Prefabs      list     Prefab names this entry applies to. Unknown names are warned about\n#                         and skipped, they do not break the file.\n#\n# A typo in a key or an enum value costs you that one setting and logs a warning naming the\n# line; the rest of the file still loads.\n#################################################",
				Defaults = () => ExampleData.BuildDefaults(),
				Apply = delegate(ExampleSettings parsed)
				{
					ExampleData.Current = parsed;
				},
				Validate = ExampleData.Validate,
				NeedsPrefabs = true,
				SchemaVersion = 1,
				GetSchemaVersion = (ExampleSettings settings) => settings.Version,
				SetSchemaVersion = delegate(ExampleSettings settings, int version)
				{
					settings.Version = version;
				}
			});
			ExampleSavedDataFile = Register(new YamlConfigFile<Dictionary<string, int>>("ExampleSavedData.yaml")
			{
				SubFolder = "SavedData",
				Header = "# Save data written by this mod. Edit it with the game closed.",
				Defaults = () => new Dictionary<string, int>(),
				Apply = delegate(Dictionary<string, int> parsed)
				{
					ExampleData.SavedCounters = parsed;
				},
				Sync = ConfigSyncMode.LocalOnly,
				Watch = false
			});
			Register(new YamlConfigFile<Dictionary<string, ExampleEntry>>("ExampleLegacyFormat.yaml")
			{
				Header = "# A config kept in its original camelCase form for backwards compatibility.",
				Format = YamlFormat.CamelCase,
				Defaults = () => new Dictionary<string, ExampleEntry>(),
				Apply = delegate(Dictionary<string, ExampleEntry> parsed)
				{
					ExampleData.LegacyEntries = parsed;
				},
				ClientWritesToDisk = true
			});
		}

		internal static void Init()
		{
			if (!initialized)
			{
				initialized = true;
				ConfigNetwork.Init();
				YamlFormat.AddTypeConverter((IYamlTypeConverter)(object)new TolerantEnumConverter());
				RegisterConfigFiles();
				ConfigFileWatcher.Initialize();
				Logger.LogDebug($"Registered {Files.Count} yaml config files.");
			}
		}

		internal static TFile Register<TFile>(TFile file) where TFile : YamlConfigFile
		{
			if (file == null)
			{
				return null;
			}
			Files.Add(file);
			if (initialized)
			{
				Prepare(file);
			}
			return file;
		}

		internal static YamlConfigFile Find(string fileNameOrPath)
		{
			if (string.IsNullOrEmpty(fileNameOrPath))
			{
				return null;
			}
			if (ByPath.TryGetValue(fileNameOrPath, out var value))
			{
				return value;
			}
			for (int i = 0; i < Files.Count; i++)
			{
				if (string.Equals(Files[i].FileName, fileNameOrPath, StringComparison.OrdinalIgnoreCase))
				{
					return Files[i];
				}
			}
			return null;
		}

		internal static string ConfigDirectory(string subFolder = null)
		{
			string text = Path.Combine(Paths.ConfigPath, ValConfig.cfgFolder);
			if (!string.IsNullOrEmpty(subFolder))
			{
				text = Path.Combine(text, subFolder);
			}
			return Directory.CreateDirectory(text).FullName;
		}

		internal static void ReloadFromDisk(YamlConfigFile file, bool broadcast = true)
		{
			if (file == null)
			{
				return;
			}
			try
			{
				if (!File.Exists(file.Path))
				{
					Logger.LogWarning(file.FileName + " is no longer on disk; rewriting it with this mod's built-in defaults.");
					RestoreDefaults(file);
				}
				if (file.LoadFrom(File.ReadAllText(file.Path), ConfigOrigin.LocalFile) && broadcast)
				{
					ConfigNetwork.Broadcast(file);
				}
			}
			catch (Exception ex)
			{
				Logger.LogError("Could not reload " + file.FileName + ": " + ex.Message);
			}
		}

		internal static bool ApplyEdited(YamlConfigFile file, string yaml, out string message)
		{
			message = "";
			if (file == null)
			{
				message = "no config file was named";
				return false;
			}
			if ((Object)(object)ZNet.instance != (Object)null && !ZNet.instance.IsServer())
			{
				message = file.FileName + " belongs to the server; changes have to be sent to it.";
				return false;
			}
			string parseError;
			ValidationReport validationReport = file.DryRun(yaml, out parseError);
			if (parseError != null)
			{
				message = file.FileName + " was rejected because " + parseError + ".";
				return false;
			}
			if (validationReport.HasErrors)
			{
				message = string.Join(" ", validationReport.Errors.ToArray());
				return false;
			}
			if (!file.LoadFrom(yaml, ConfigOrigin.Api))
			{
				message = file.LastError ?? (file.FileName + " could not be applied.");
				return false;
			}
			WriteRawToDisk(file, yaml);
			ConfigNetwork.Broadcast(file);
			message = ((validationReport.Warnings.Count == 0) ? "" : string.Join(" ", validationReport.Warnings.ToArray()));
			return true;
		}

		internal static string SerializeForEdit<T>(YamlConfigFile<T> file, T value) where T : class
		{
			if (file == null || value == null)
			{
				return "";
			}
			return file.EffectiveFormat.Serializer.Serialize((object)value);
		}

		internal static void RestoreDefaults(YamlConfigFile file)
		{
			WriteRawToDisk(file, file?.SerializeDefaults());
		}

		internal static void WriteCurrentToDisk(YamlConfigFile file)
		{
			WriteRawToDisk(file, file?.SerializeCurrent());
		}

		internal static void WriteRawToDisk(YamlConfigFile file, string serializedYaml)
		{
			if (file == null || string.IsNullOrEmpty(file.Path))
			{
				return;
			}
			try
			{
				Directory.CreateDirectory(Path.GetDirectoryName(file.Path));
				using (StreamWriter streamWriter = new StreamWriter(file.Path))
				{
					if (!string.IsNullOrEmpty(file.Header))
					{
						streamWriter.WriteLine(file.Header);
					}
					streamWriter.WriteLine(serializedYaml);
				}
				ConfigFileWatcher.RefreshStamp(file.Path);
			}
			catch (Exception ex)
			{
				Logger.LogError("Could not write " + file.FileName + ": " + ex.Message);
			}
		}

		internal static void RevalidateAll()
		{
			for (int i = 0; i < Files.Count; i++)
			{
				try
				{
					Files[i].Revalidate();
				}
				catch (Exception ex)
				{
					Logger.LogError("Revalidating " + Files[i].FileName + " threw: " + ex.Message);
				}
			}
		}

		internal static bool HasNoUsableConfig(string yamlText)
		{
			if (string.IsNullOrWhiteSpace(yamlText))
			{
				return true;
			}
			string[] array = yamlText.Split(new char[1] { '\n' });
			for (int i = 0; i < array.Length; i++)
			{
				string text = array[i].Trim();
				if (text.Length != 0 && !text.StartsWith("#") && !(text == "---") && !(text == "..."))
				{
					return false;
				}
			}
			return true;
		}

		private static void Prepare(YamlConfigFile file)
		{
			try
			{
				file.Path = Path.Combine(ConfigDirectory(file.SubFolder), file.FileName);
				ByPath[file.Path] = file;
				if (!File.Exists(file.Path))
				{
					Logger.LogDebug(file.FileName + " missing, writing this mod's built-in defaults.");
					RestoreDefaults(file);
				}
				else if (HasNoUsableConfig(File.ReadAllText(file.Path)))
				{
					Logger.LogWarning(file.FileName + " was empty and has been overwritten with this mod's built-in defaults. File: " + file.Path);
					RestoreDefaults(file);
				}
				file.LoadFrom(File.Exists(file.Path) ? File.ReadAllText(file.Path) : "", ConfigOrigin.Startup);
				ConfigNetwork.RegisterFile(file);
				if (file.Watch)
				{
					ConfigFileWatcher.Register(file.Path, OnWatchedFileChanged);
				}
			}
			catch (Exception arg)
			{
				Logger.LogError($"Could not prepare {file.FileName}: {arg}");
			}
		}

		private static void OnWatchedFileChanged(string path)
		{
			if (ByPath.TryGetValue(path, out var file))
			{
				ConfigChangeDebouncer.Schedule(file, delegate
				{
					ReloadFromDisk(file);
				});
			}
		}
	}
	public enum ExampleMode
	{
		Off,
		Add,
		Multiply
	}
	public class ExampleEntry
	{
		public string DisplayName { get; set; }

		[DefaultValue(1f)]
		public float Multiplier { get; set; } = 1f;

		public ExampleMode Mode { get; set; }

		public List<string> Prefabs { get; set; } = new List<string>();
	}
	public class ExampleSettings
	{
		public int Version { get; set; } = 1;

		public Dictionary<string, ExampleEntry> Entries { get; set; } = new Dictionary<string, ExampleEntry>();
	}
	internal static class ExampleData
	{
		internal static ExampleSettings Current = new ExampleSettings();

		internal static Dictionary<string, int> SavedCounters = new Dictionary<string, int>();

		internal static Dictionary<string, ExampleEntry> LegacyEntries = new Dictionary<string, ExampleEntry>();

		internal static ExampleSettings BuildDefaults()
		{
			return new ExampleSettings
			{
				Version = 1,
				Entries = new Dictionary<string, ExampleEntry> { 
				{
					"Example",
					new ExampleEntry
					{
						DisplayName = "An example",
						Multiplier = 1.5f,
						Mode = ExampleMode.Multiply
					}
				} }
			};
		}

		internal static ValidationReport Validate(ExampleSettings next, ExampleSettings previous)
		{
			ValidationReport validationReport = new ValidationReport();
			if (next.Entries == null || next.Entries.Count == 0)
			{
				return validationReport.Error("it defines no entries");
			}
			foreach (KeyValuePair<string, ExampleEntry> entry in next.Entries)
			{
				if (entry.Value == null)
				{
					validationReport.Error("entry '" + entry.Key + "' has no settings under it");
					continue;
				}
				if (entry.Value.Multiplier < 0.1f || entry.Value.Multiplier > 10f)
				{
					validationReport.Warn($"entry '{entry.Key}' has Multiplier {entry.Value.Multiplier}, outside the " + "supported range of 0.1 - 10. It will be used as written.");
				}
				if (entry.Value.Prefabs == null)
				{
					continue;
				}
				foreach (string prefab in entry.Value.Prefabs)
				{
					if (PrefabManager.Instance != null && (Object)(object)PrefabManager.Instance.GetPrefab(prefab) == (Object)null)
					{
						validationReport.Warn("entry '" + entry.Key + "' names prefab '" + prefab + "', which does not exist. That prefab will be skipped.");
					}
				}
			}
			if (previous != null && previous.Entries != null)
			{
				foreach (string key in previous.Entries.Keys)
				{
					if (!next.Entries.ContainsKey(key))
					{
						validationReport.Warn("entry '" + key + "' was removed. Anything still referring to it will fall back.");
					}
				}
			}
			return validationReport;
		}
	}
	internal class TolerantEnumConverter : IYamlTypeConverter
	{
		private static readonly Dictionary<Type, object> fallbacks = new Dictionary<Type, object>();

		internal static void SetFallback(Type enumType, object fallback)
		{
			if (!(enumType == null) && enumType.IsEnum)
			{
				fallbacks[enumType] = fallback;
			}
		}

		public bool Accepts(Type type)
		{
			if (type != null)
			{
				return type.IsEnum;
			}
			return false;
		}

		public object ReadYaml(IParser parser, Type type, ObjectDeserializer rootDeserializer)
		{
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			Scalar val = ParserExtensions.Consume<Scalar>(parser);
			string value = val.Value;
			if (!string.IsNullOrWhiteSpace(value))
			{
				try
				{
					return Enum.Parse(type, value.Trim(), ignoreCase: true);
				}
				catch (Exception)
				{
				}
			}
			object obj = FallbackFor(type);
			object[] array = new object[4];
			Mark start = ((ParsingEvent)val).Start;
			array[0] = ((Mark)(ref start)).Line;
			array[1] = value;
			array[2] = type.Name;
			array[3] = obj;
			Logger.LogWarning(string.Format("line {0}: '{1}' is not a valid {2}. Using {3}. ", array) + "Valid values: " + string.Join(", ", Enum.GetNames(type)) + ".");
			return obj;
		}

		public void WriteYaml(IEmitter emitter, object value, Type type, ObjectSerializer serializer)
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Expected O, but got Unknown
			emitter.Emit((ParsingEvent)new Scalar((value == null) ? "" : value.ToString()));
		}

		private static object FallbackFor(Type type)
		{
			if (fallbacks.TryGetValue(type, out var value))
			{
				return value;
			}
			return Activator.CreateInstance(type);
		}
	}
	internal static class ConfigUI
	{
		internal class ConfigUIInputGuard : MonoBehaviour
		{
			private bool held;

			internal void Hold()
			{
				if (!held)
				{
					held = true;
					PushInputBlock();
				}
			}

			public void OnDestroy()
			{
				if (held)
				{
					held = false;
					PopInputBlock();
				}
			}
		}

		internal const float RowHeight = 34f;

		internal const float SubRowHeight = 26f;

		internal const float RowGap = 4f;

		internal const float CloseXSize = 28f;

		private static int inputBlockDepth;

		internal static void PushInputBlock()
		{
			if (!((Object)(object)Player.m_localPlayer == (Object)null))
			{
				inputBlockDepth++;
				if (inputBlockDepth == 1)
				{
					GUIManager.BlockInput(true);
				}
			}
		}

		internal static void PopInputBlock()
		{
			if (inputBlockDepth > 0)
			{
				inputBlockDepth--;
				if (inputBlockDepth == 0)
				{
					GUIManager.BlockInput(false);
				}
			}
		}

		internal static GameObject NewUI(string name, Transform parent, params Type[] components)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Expected O, but got Unknown
			GameObject val = new GameObject(name, components)
			{
				layer = 5
			};
			if ((Object)(object)val.GetComponent<RectTransform>() == (Object)null)
			{
				val.AddComponent<RectTransform>();
			}
			val.transform.SetParent(parent, false);
			return val;
		}

		internal static GameObject NewRect(string name, Transform parent, float x, float y, float w, float h)
		{
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: 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_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: 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)
			GameObject obj = NewUI(name, parent, typeof(RectTransform));
			RectTransform val = (RectTransform)obj.transform;
			val.anchorMin = new Vector2(0f, 1f);
			val.anchorMax = new Vector2(0f, 1f);
			val.pivot = new Vector2(0f, 1f);
			val.sizeDelta = new Vector2(w, h);
			val.anchoredPosition = new Vector2(x, 0f - y);
			return obj;
		}

		internal static GameObject NewRow(Transform parent, float width, float height)
		{
			return NewRect("Row", parent, 0f, 0f, width, height);
		}

		internal static GameObject NewLayoutRow(Transform content, float width, float height)
		{
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			GameObject obj = NewUI("LayoutRow", content, typeof(RectTransform), typeof(LayoutElement));
			RectTransform val = (RectTransform)obj.transform;
			val.anchorMin = new Vector2(0f, 1f);
			val.anchorMax = new Vector2(0f, 1f);
			val.pivot = new Vector2(0f, 1f);
			val.sizeDelta = new Vector2(width, height);
			LayoutElement component = obj.GetComponent<LayoutElement>();
			component.minHeight = height;
			component.preferredHeight = height;
			component.minWidth = width;
			component.preferredWidth = width;
			return obj;
		}

		internal static void LayoutColumn(List<GameObject> rows, float x, float startY, float gap = 4f)
		{
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Expected O, but got Unknown
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			float num = startY;
			foreach (GameObject row in rows)
			{
				if (!((Object)(object)row == (Object)null) && row.activeSelf)
				{
					RectTransform val = (RectTransform)row.transform;
					val.anchoredPosition = new Vector2(x, 0f - num);
					num += val.sizeDelta.y + gap;
				}
			}
		}

		internal static void PositionRow(GameObject row, float x, float y)
		{
			//IL_0010: 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)
			if (!((Object)(object)row == (Object)null))
			{
				((RectTransform)row.transform).anchoredPosition = new Vector2(x, 0f - y);
			}
		}

		internal static GameObject CreatePanel(string title, float w, float h, out Transform body)
		{
			//IL_0019: 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_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = GUIManager.Instance.CreateWoodpanel(GUIManager.CustomGUIFront.transform, new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), new Vector2(0f, 0f), w, h, true);
			val.AddComponent<ConfigUIInputGuard>().Hold();
			AddText(val.transform, 0f, 16f, w, 34f, title, 22, (TextAnchor)4, GUIManager.Instance.ValheimYellow);
			body = val.transform;
			return val;
		}

		internal static GameObject CreateScroll(Transform parent, float x, float y, float w, float h, out Transform content, out float contentWidth)
		{
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = NewRect("ScrollHolder", parent, x, y, w, h);
			GameObject val2 = GUIManager.Instance.CreateScrollView(val.transform, false, true, 8f, 4f, GUIManager.Instance.ValheimScrollbarHandleColorBlock, new Color(0f, 0f, 0f, 0.5f), w, h);
			content = val2.transform.Find("Scroll View/Viewport/Content");
			contentWidth = w - 16f;
			ScrollRect componentInChildren = val2.GetComponentInChildren<ScrollRect>();
			if ((Object)(object)componentInChildren != (Object)null)
			{
				componentInChildren.scrollSensitivity = 200f;
			}
			return val2;
		}

		internal static string L(string text)
		{
			if (string.IsNullOrEmpty(text))
			{
				return "";
			}
			if (Localization.instance == null)
			{
				return text;
			}
			return Localization.instance.Localize(text);
		}

		internal static Text AddText(Transform parent, float x, float y, float w, float h, string text, int fontSize, TextAnchor anchor, Color? color = null)
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: 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)
			//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
			GameObject obj = GUIManager.Instance.CreateText(L(text), parent, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(x, 0f - y), GUIManager.Instance.AveriaSerifBold, fontSize, (Color)(((??)color) ?? GUIManager.Instance.ValheimBeige), true, Color.black, w, h, false);
			RectTransform val = (RectTransform)obj.transform;
			val.pivot = new Vector2(0f, 1f);
			val.anchoredPosition = new Vector2(x, 0f - y);
			Text component = obj.GetComponent<Text>();
			component.alignment = anchor;
			component.horizontalOverflow = (HorizontalWrapMode)0;
			component.verticalOverflow = (VerticalWrapMode)0;
			return component;
		}

		internal static GameObject AddHeaderRow(Transform parent, float colWidth, string text, TextAnchor anchor = (TextAnchor)3)
		{
			//IL_0025: 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)
			GameObject obj = NewRow(parent, colWidth, 34f);
			AddText(obj.transform, 0f, 0f, colWidth, 34f, text, 18, anchor, GUIManager.Instance.ValheimYellow);
			return obj;
		}

		internal static GameObject AddTextRow(Transform parent, float colWidth, float height, string text, int fontSize, Color color, TextAnchor anchor = (TextAnchor)0)
		{
			//IL_001d: 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)
			GameObject obj = NewRow(parent, colWidth, height);
			AddText(obj.transform, 0f, 0f, colWidth, height, text, fontSize, anchor, color);
			return obj;
		}

		internal static GameObject AddSpacerRow(Transform parent, float colWidth, float height)
		{
			return NewRow(parent, colWidth, height);
		}

		internal static GameObject AddDividerRow(Transform parent, float colWidth, float height = 12f)
		{
			//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_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: 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_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: 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_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = NewRow(parent, colWidth, height);
			GameObject obj = NewUI("Divider", val.transform, typeof(Image));
			RectTransform val2 = (RectTransform)obj.transform;
			val2.anchorMin = new Vector2(0f, 1f);
			val2.anchorMax = new Vector2(0f, 1f);
			val2.pivot = new Vector2(0f, 1f);
			val2.sizeDelta = new Vector2(colWidth, 2f);
			val2.anchoredPosition = new Vector2(0f, 0f - height * 0.5f);
			Image component = obj.GetComponent<Image>();
			((Graphic)component).color = new Color(0.6f, 0.5f, 0.35f, 0.6f);
			((Graphic)component).raycastTarget = false;
			return val;
		}

		internal static void SetMessages(Text target, IList<string> errors, IList<string> warnings)
		{
			if ((Object)(object)target == (Object)null)
			{
				return;
			}
			List<string> list = new List<string>();
			if (errors != null)
			{
				foreach (string error in errors)
				{
					list.Add("<color=#F87171>" + error + "</color>");
				}
			}
			if (warnings != null)
			{
				foreach (string warning in warnings)
				{
					list.Add("<color=#FBBF24>" + warning + "</color>");
				}
			}
			target.text = string.Join("\n", list.ToArray());
		}

		internal static GameObject AddButton(Transform parent, float x, float y, float w, string text, UnityAction onClick, float h = 40f)
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//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_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = GUIManager.Instance.CreateButton(L(text), parent, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(x, 0f - y), w, h);
			RectTransform val2 = (RectTransform)val.transform;
			val2.pivot = new Vector2(0f, 1f);
			val2.anchoredPosition = new Vector2(x, 0f - y);
			if (onClick != null)
			{
				((UnityEvent)val.GetComponent<Button>().onClick).AddListener(onClick);
			}
			return val;
		}

		internal static GameObject AddCloseX(Transform panel, float panelWidth, UnityAction onClick)
		{
			return AddButton(panel, panelWidth - 28f - 16f, 12f, 28f, "X", onClick, 28f);
		}

		internal static Toggle AddToggle(Transform parent, float x, float y, float size, bool value, Action<bool> onChange)
		{
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: 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_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			GameObject obj = GUIManager.Instance.CreateToggle(parent, size, size);
			obj.transform.SetParent(parent, false);
			RectTransform val = (RectTransform)obj.transform;
			((Transform)val).localScale = Vector3.one;
			val.anchorMin = new Vector2(0f, 1f);
			val.anchorMax = new Vector2(0f, 1f);
			val.pivot = new Vector2(0f, 1f);
			val.anchoredPosition = new Vector2(x, 0f - y);
			Toggle component = obj.GetComponent<Toggle>();
			component.isOn = value;
			if (onChange != null)
			{
				((UnityEvent<bool>)(object)component.onValueChanged).AddListener((UnityAction<bool>)delegate(bool b)
				{
					onChange(b);
				});
			}
			return component;
		}

		internal static GameObject AddToggleRow(Transform parent, float colWidth, float labelW, string label, bool value, Action<bool> onChange, bool toggleOnLeft = false)
		{
			GameObject obj = NewRow(parent, colWidth, 34f);
			float x = (toggleOnLeft ? 0f : (labelW + 6f));
			float x2 = (toggleOnLeft ? 34f : 0f);
			AddText(obj.transform, x2, 0f, labelW, 34f, label, 15, (TextAnchor)3);
			AddToggle(obj.transform, x, 3f, 26f, value, onChange);
			return obj;
		}

		internal static string Fmt(float v, bool whole)
		{
			if (!whole)
			{
				return v.ToString("0.00");
			}
			return ((int)Mathf.Round(v)).ToString();
		}

		internal static Slider BuildSlider(Transform parent, float x, float y, float width, float min, float max, float value, bool wholeNumbers)
		{
			//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_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: 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_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: 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_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ec: 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_00f7: Unknown result type (might be due to invalid IL or missing references)
			//IL_011a: Unknown result type (might be due to invalid IL or missing references)
			//IL_014e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0153: Unknown result type (might be due to invalid IL or missing references)
			//IL_015e: 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_0173: Unknown result type (might be due to invalid IL or missing references)
			//IL_017d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0188: Unknown result type (might be due to invalid IL or missing references)
			//IL_0192: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cb: Expected O, but got Unknown
			//IL_01d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f9: Unknown result type (might be due to invalid IL or missing references)
			//IL_022d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0232: Unknown result type (might be due to invalid IL or missing references)
			//IL_0233: Unknown result type (might be due to invalid IL or missing references)
			//IL_023d: Unknown result type (might be due to invalid IL or missing references)
			//IL_023e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0248: Unknown result type (might be due to invalid IL or missing references)
			//IL_0253: Unknown result type (might be due to invalid IL or missing references)
			//IL_025d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0290: Unknown result type (might be due to invalid IL or missing references)
			//IL_0297: Expected O, but got Unknown
			//IL_02a3: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = NewUI("Slider", parent, typeof(RectTransform), typeof(Slider));
			RectTransform val2 = (RectTransform)val.transform;
			val2.anchorMin = new Vector2(0f, 1f);
			val2.anchorMax = new Vector2(0f, 1f);
			val2.pivot = new Vector2(0f, 1f);
			val2.sizeDelta = new Vector2(width, 20f);
			val2.anchoredPosition = new Vector2(x, 0f - y);
			GameObject obj = NewUI("Background", val.transform, typeof(Image));
			RectTransform val3 = (RectTransform)obj.transform;
			val3.anchorMin = new Vector2(0f, 0.25f);
			val3.anchorMax = new Vector2(1f, 0.75f);
			val3.sizeDelta = Vector2.zero;
			val3.anchoredPosition = Vector2.zero;
			((Graphic)obj.GetComponent<Image>()).color = new Color(0f, 0f, 0f, 0.6f);
			GameObject val4 = NewUI("Fill Area", val.transform, typeof(RectTransform));
			RectTransform val5 = (RectTransform)val4.transform;
			val5.anchorMin = new Vector2(0f, 0.25f);
			val5.anchorMax = new Vector2(1f, 0.75f);
			val5.sizeDelta = new Vector2(-20f, 0f);
			val5.anchoredPosition = Vector2.zero;
			GameObject obj2 = NewUI("Fill", val4.transform, typeof(Image));
			RectTransform val6 = (RectTransform)obj2.transform;
			val6.sizeDelta = new Vector2(10f, 0f);
			((Graphic)obj2.GetComponent<Image>()).color = new Color(0.7f, 0.6f, 0.4f, 0.9f);
			GameObject val7 = NewUI("Handle Slide Area", val.transform, typeof(RectTransform));
			RectTransform val8 = (RectTransform)val7.transform;
			val8.anchorMin = Vector2.zero;
			val8.anchorMax = Vector2.one;
			val8.sizeDelta = new Vector2(-20f, 0f);
			val8.anchoredPosition = Vector2.zero;
			GameObject obj3 = NewUI("Handle", val7.transform, typeof(Image));
			RectTransform val9 = (RectTransform)obj3.transform;
			val9.sizeDelta = new Vector2(20f, 0f);
			Image component = obj3.GetComponent<Image>();
			component.sprite = GUIManager.Instance.GetSprite("checkbox_marker");
			component.type = (Type)1;
			Slider component2 = val.GetComponent<Slider>();
			component2.fillRect = val6;
			component2.handleRect = val9;
			((Selectable)component2).targetGraphic = (Graphic)(object)component;
			component2.direction = (Direction)0;
			component2.minValue = min;
			component2.maxValue = max;
			component2.wholeNumbers = wholeNumbers;
			component2.value = Mathf.Clamp(value, min, max);
			GUIManager.Instance.ApplySliderStyle(component2);
			return component2;
		}

		internal static GameObject AddSliderRow(Transform parent, float colWidth, float labelW, float sliderW, float valueW, string label, float min, float max, float value, bool wholeNumbers, Action<float> onChange)
		{
			//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ef: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_010a: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = NewRow(parent, colWidth, 34f);
			AddText(val.transform, 0f, 0f, labelW, 34f, label, 15, (TextAnchor)3);
			Slider slider = BuildSlider(val.transform, labelW, 7f, sliderW, min, max, value, wholeNumbers);
			float num = labelW + sliderW + 10f;
			GameObject val2 = GUIManager.Instance.CreateInputField(val.transform, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(num, -3f), (ContentType)(wholeNumbers ? 2 : 3), (string)null, 15, valueW, 28f);
			RectTransform val3 = (RectTransform)val2.transform;
			val3.pivot = new Vector2(0f, 1f);
			val3.anchoredPosition = new Vector2(num, -3f);
			InputField box = val2.GetComponent<InputField>();
			box.SetTextWithoutNotify(Fmt(slider.value, wholeNumbers));
			((UnityEvent<float>)(object)slider.onValueChanged).AddListener((UnityAction<float>)delegate(float v)
			{
				if (wholeNumbers)
				{
					v = Mathf.Round(v);
				}
				box.SetTextWithoutNotify(Fmt(v, wholeNumbers));
				onChange?.Invoke(v);
			});
			((UnityEvent<string>)(object)box.onEndEdit).AddListener((UnityAction<string>)delegate(string str)
			{
				if (!float.TryParse(str, out var result))
				{
					result = slider.value;
				}
				result = Mathf.Clamp(result, min, max);
				if (wholeNumbers)
				{
					result = Mathf.Round(result);
				}
				box.SetTextWithoutNotify(Fmt(result, wholeNumbers));
				if (slider.value != result)
				{
					slider.value = result;
				}
				else
				{
					onChange?.Invoke(result);
				}
			});
			return val;
		}

		internal static InputField AddTextField(Transform parent, float x, float y, float w, string value, Action<string> onCommit, ContentType contentType = (ContentType)0, string placeholder = null, int charLimit = 0)
		{
			//IL_001e: 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_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: 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_006e: Unknown result type (might be due to invalid IL or missing references)
			GameObject obj = GUIManager.Instance.CreateInputField(parent, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(x, 0f - y), contentType, placeholder, 15, w, 28f);
			RectTransform val = (RectTransform)obj.transform;
			val.pivot = new Vector2(0f, 1f);
			val.anchoredPosition = new Vector2(x, 0f - y);
			InputField component = obj.GetComponent<InputField>();
			if (charLimit > 0)
			{
				component.characterLimit = charLimit;
			}
			component.SetTextWithoutNotify(value ?? "");
			if (onCommit != null)
			{
				((UnityEvent<string>)(object)component.onEndEdit).AddListener((UnityAction<string>)delegate(string s)
				{
					onCommit(s);
				});
			}
			return component;
		}

		internal static GameObject AddTextFieldRow(Transform parent, float colWidth, float labelW, float fieldW, string label, string value, Action<string> onCommit, string placeholder = null, int charLimit = 0)
		{
			GameObject obj = NewRow(parent, colWidth, 34f);
			AddText(obj.transform, 0f, 0f, labelW, 34f, label, 15, (TextAnchor)3);
			AddTextField(obj.transform, labelW + 6f, 3f, fieldW, value, onCommit, (ContentType)0, placeholder, charLimit);
			return obj;
		}

		internal static GameObject AddEnumCycleRow(Transform parent, float colWidth, float labelW, float ctrlW, string label, string[] options, int currentIndex, Action<int> onChange)
		{
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_0122: Unknown result type (might be due to invalid IL or missing references)
			//IL_012c: Expected O, but got Unknown
			GameObject val = NewRow(parent, colWidth, 34f);
			AddText(val.transform, 0f, 0f, labelW, 34f, label, 15, (TextAnchor)3);
			int idx = Mathf.Clamp(currentIndex, 0, Math.Max(0, options.Length - 1));
			GameObject val2 = GUIManager.Instance.CreateButton((options.Length != 0) ? options[idx] : "", val.transform, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(labelW + 6f, -2f), ctrlW, 28f);
			RectTransform val3 = (RectTransform)val2.transform;
			val3.pivot = new Vector2(0f, 1f);
			val3.anchoredPosition = new Vector2(labelW + 6f, -2f);
			Text caption = val2.GetComponentInChildren<Text>();
			((UnityEvent)val2.GetComponent<Button>().onClick).AddListener((UnityAction)delegate
			{
				if (options.Length != 0)
				{
					idx = (idx + 1) % options.Length;
					caption.text = options[idx];
					onChange?.Invoke(idx);
				}
			});
			return val;
		}

		internal static GameObject AddEnumFlagsRow(Transform parent, float colWidth, float labelW, string label, string[] names, Func<int, bool> isOn, Action<int, bool> set)
		{
			GameObject val = NewRow(parent, colWidth, 34f);
			AddText(val.transform, 0f, 0f, labelW, 34f, label, 15, (TextAnchor)3);
			float num = labelW + 6f;
			for (int i = 0; i < names.Length; i++)
			{
				int index = i;
				AddToggle(val.transform, num, 4f, 22f, isOn(index), delegate(bool on)
				{
					set(index, on);
				});
				AddText(val.transform, num + 26f, 0f, 90f, 34f, names[index], 13, (TextAnchor)3);
				num += 120f;
			}
			return val;
		}

		internal static GameObject AddPickerRow(Transform parent, float colWidth, float labelW, float ctrlW, string label, string current, Func<IList<string>> options, Action<string> onPick, Func<string, bool> isKnown = null)
		{
			//IL_00d4: Unknown result type (might be due to invalid IL or missing references)
			//IL_014c: Unknown result type (might be due to invalid IL or missing references)
			//IL_015b: Expected O, but got Unknown
			GameObject val = NewRow(parent, colWidth, 34f);
			AddText(val.transform, 0f, 0f, labelW, 34f, label, 15, (TextAnchor)3);
			float num = ctrlW - 40f;
			InputField field = AddTextField(val.transform, labelW + 6f, 3f, num, current, delegate(string s)
			{
				onPick?.Invoke(s);
			}, (ContentType)0);
			Text marker = AddText(val.transform, labelW + 6f + num + 44f, 0f, 20f, 34f, "", 15, (TextAnchor)3, (Color?)new Color(0.98f, 0.75f, 0.14f));
			Action refreshMarker = delegate
			{
				bool flag = isKnown != null && !string.IsNullOrEmpty(field.text) && !isKnown(field.text);
				marker.text = (flag ? "!" : "");
			};
			refreshMarker();
			((UnityEvent<string>)(object)field.onEndEdit).AddListener((UnityAction<string>)delegate
			{
				refreshMarker();
			});
			AddButton(val.transform, labelW + 6f + num + 4f, 3f, 34f, "...", (UnityAction)delegate
			{
				string title = label;
				IList<string> options2;
				if (options == null)
				{
					IList<string> list = new List<string>();
					options2 = list;
				}
				else
				{
					options2 = options();
				}
				ConfigUIPicker.ShowPicker(title, options2, field.text, delegate(string picked)
				{
					field.SetTextWithoutNotify(picked);
					refreshMarker();
					onPick?.Invoke(picked);
				});
			}, 28f);
			return val;
		}

		internal static GameObject AddStringListEditor(Transform content, float width, string label, List<string> items, Action onChanged, Func<IList<string>> options = null, Func<string, bool> isKnown = null)
		{
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a5: Expected O, but got Unknown
			//IL_014c: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fe: Unknown result type (might be due to invalid IL or missing references)
			//IL_020d: Expected O, but got Unknown
			//IL_01ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d9: Expected O, but got Unknown
			GameObject val = NewLayoutRow(content, width, 26f);
			AddText(val.transform, 0f, 0f, width - 90f, 26f, label, 14, (TextAnchor)3, GUIManager.Instance.ValheimOrange);
			AddButton(val.transform, width - 84f, 0f, 80f, "Add", (UnityAction)delegate
			{
				items.Add("");
				onChanged?.Invoke();
			}, 24f);
			for (int num = 0; num < items.Count; num++)
			{
				int index = num;
				GameObject val2 = NewLayoutRow(content, width, 26f);
				InputField field = AddTextField(val2.transform, 12f, 0f, width - 92f, items[index], delegate(string s)
				{
					items[index] = s;
				}, (ContentType)0);
				Text marker = AddText(val2.transform, width - 74f, 0f, 16f, 26f, "", 14, (TextAnchor)3, (Color?)new Color(0.98f, 0.75f, 0.14f));
				Action refreshMarker = delegate
				{
					bool flag = isKnown != null && !string.IsNullOrEmpty(field.text) && !isKnown(field.text);
					marker.text = (flag ? "!" : "");
				};
				refreshMarker();
				((UnityEvent<string>)(object)field.onEndEdit).AddListener((UnityAction<string>)delegate
				{
					refreshMarker();
				});
				if (options != null)
				{
					AddButton(val2.transform, width - 56f, 0f, 26f, "...", (UnityAction)delegate
					{
						ConfigUIPicker.ShowPicker(label, options(), field.text, delegate(string picked)
						{
							field.SetTextWithoutNotify(picked);
							items[index] = picked;
							refreshMarker();
						});
					}, 24f);
				}
				AddButton(val2.transform, width - 26f, 0f, 24f, "x", (UnityAction)delegate
				{
					items.RemoveAt(index);
					onChanged?.Invoke();
				}, 24f);
			}
			return val;
		}
	}
	internal static class ConfigUIPicker
	{
		private const float PanelW = 420f;

		private const float PanelH = 520f;

		private const float RowH = 30f;

		private const int MaxRows = 400;

		private static GameObject overlay;

		internal static bool IsOpen => (Object)(object)overlay != (Object)null;

		internal static void ShowPicker(string title, IList<string> options, string current, Action<string> onPick)
		{
			//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0101: Unknown result type (might be due to invalid IL or missing references)
			//IL_0128: Unknown result type (might be due to invalid IL or missing references)
			//IL_0132: Expected O, but got Unknown
			//IL_015a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0169: Unknown result type (might be due to invalid IL or missing references)
			//IL_0178: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_0209: Unknown result type (might be due to invalid IL or missing references)
			//IL_0218: Expected O, but got Unknown
			Close();
			if (GUIManager.Instance == null || (Object)(object)GUIManager.CustomGUIFront == (Object)null)
			{
				return;
			}
			List<string> all = new List<string>();
			if (options != null)
			{
				foreach (string option in options)
				{
					if (!string.IsNullOrEmpty(option))
					{
						all.Add(option);
					}
				}
			}
			all.Sort(StringComparer.OrdinalIgnoreCase);
			overlay = ConfigUI.NewUI("ConfigUIPickerOverlay", GUIManager.CustomGUIFront.transform, typeof(Image));
			RectTransform val = (RectTransform)overlay.transform;
			val.anchorMin = Vector2.zero;
			val.anchorMax = Vector2.one;
			val.offsetMin = Vector2.zero;
			val.offsetMax = Vector2.zero;
			((Graphic)overlay.GetComponent<Image>()).color = new Color(0f, 0f, 0f, 0.35f);
			Button obj = overlay.AddComponent<Button>();
			((Selectable)obj).transition = (Transition)0;
			((UnityEvent)obj.onClick).AddListener(new UnityAction(Close));
			overlay.AddComponent<ConfigUI.ConfigUIInputGuard>().Hold();
			GameObject obj2 = GUIManager.Instance.CreateWoodpanel(overlay.transform, new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), new Vector2(0f, 0f), 420f, 520f, true);
			ConfigUI.AddText(obj2.transform, 0f, 14f, 420f, 30f, title, 18, (TextAnchor)4, GUIManager.Instance.ValheimYellow);
			InputField val2 = ConfigUI.AddTextField(obj2.transform, 16f, 52f, 320f, "", null, (ContentType)0, "Filter...");
			ConfigUI.AddButton(obj2.transform, 344f, 52f, 60f, "Close", new UnityAction(Close), 28f);
			ConfigUI.CreateScroll(obj2.transform, 16f, 92f, 388f, 404f, out var content, out var contentW);
			if ((Object)(object)content == (Object)null)
			{
				return;
			}
			Action<string> rebuild = null;
			rebuild = delegate(string needle)
			{
				//IL_0014: Unknown result type (might be due to invalid IL or missing references)
				//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
				//IL_00dd: Expected O, but got Unknown
				foreach (Transform item in content)
				{
					Object.Destroy((Object)(object)((Component)item).gameObject);
				}
				int num = 0;
				foreach (string item2 in all)
				{
					if (string.IsNullOrEmpty(needle) || item2.IndexOf(needle, StringComparison.OrdinalIgnoreCase) >= 0)
					{
						if (num >= 400)
						{
							break;
						}
						num++;
						string picked = item2;
						ConfigUI.AddButton(ConfigUI.NewLayoutRow(content, contentW, 30f).transform, 0f, 0f, contentW, picked, (UnityAction)delegate
						{
							Close();
							onPick?.Invoke(picked);
						}, 28f);
					}
				}
				if (num == 0)
				{
					ConfigUI.AddText(ConfigUI.NewLayoutRow(content, contentW, 30f).transform, 4f, 0f, contentW, 30f, "No matches", 14, (TextAnchor)3);
				}
				else if (num >= 400)
				{
					ConfigUI.AddText(ConfigUI.NewLayoutRow(content, contentW, 30f).transform, 4f, 0f, contentW, 30f, $"... {all.Count - num} more, type to narrow", 13, (TextAnchor)3);
				}
			};
			((UnityEvent<string>)(object)val2.onValueChanged).AddListener((UnityAction<string>)delegate(string needle)
			{
				rebuild(needle);
			});
			rebuild("");
		}

		internal static void Close()
		{
			if (!((Object)(object)overlay == (Object)null))
			{
				Object.Destroy((Object)(object)overlay);
				overlay = null;
			}
		}
	}
	internal static class ConfigUIPrompt
	{
		private const float PanelW = 460f;

		private static GameObject overlay;

		internal static void Show(string title, string label, string initial, string warning, Action<string> onAccept)
		{
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: 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)
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			//IL_009c: 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_00ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_0100: Unknown result type (might be due to invalid IL or missing references)
			//IL_010f: Unknown result type (might be due to invalid IL or missing references)
			//IL_011e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0155: Unknown result type (might be due to invalid IL or missing references)
			//IL_022b: Unknown result type (might be due to invalid IL or missing references)
			//IL_023a: Expected O, but got Unknown
			//IL_025a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0269: Expected O, but got Unknown
			//IL_019c: Unknown result type (might be due to invalid IL or missing references)
			Close();
			if (GUIManager.Instance != null && !((Object)(object)GUIManager.CustomGUIFront == (Object)null))
			{
				bool flag = !string.IsNullOrEmpty(warning);
				float num = (flag ? 96f : 0f);
				float num2 = 190f + num;
				overlay = ConfigUI.NewUI("ConfigUIPromptOverlay", GUIManager.CustomGUIFront.transform, typeof(Image));
				RectTransform val = (RectTransform)overlay.transform;
				val.anchorMin = Vector2.zero;
				val.anchorMax = Vector2.one;
				val.offsetMin = Vector2.zero;
				val.offsetMax = Vector2.zero;
				((Graphic)overlay.GetComponent<Image>()).color = new Color(0f, 0f, 0f, 0.45f);
				overlay.AddComponent<ConfigUI.ConfigUIInputGuard>().Hold();
				GameObject val2 = GUIManager.Instance.CreateWoodpanel(overlay.transform, new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), new Vector2(0f, 0f), 460f, num2, true);
				ConfigUI.AddText(val2.transform, 0f, 14f, 460f, 30f, title, 18, (TextAnchor)4, GUIManager.Instance.ValheimYellow);
				float num3 = 56f;
				if (flag)
				{
					ConfigUI.AddText(val2.transform, 20f, num3, 420f, num - 8f, warning, 13, (TextAnchor)0, (Color?)new Color(0.98f, 0.75f, 0.14f));
					num3 += num;
				}
				ConfigUI.AddText(val2.transform, 20f, num3, 120f, 28f, label, 15, (TextAnchor)3);
				InputField field = ConfigUI.AddTextField(val2.transform, 140f, num3, 300f, initial, null, (ContentType)0, null, 64);
				num3 += 46f;
				ConfigUI.AddButton(val2.transform, 20f, num3, 190f, "Cancel", new UnityAction(Close), 34f);
				ConfigUI.AddButton(val2.transform, 250f, num3, 190f, "OK", (UnityAction)delegate
				{
					string text = field.text;
					Close();
					onAccept?.Invoke(text);
				}, 34f);
				((Selectable)field).Select();
				field.ActivateInputField();
			}
		}

		internal static void Close()
		{
			if (!((Object)(object)overlay == (Object)null))
			{
				Object.Destroy((Object)(object)overlay);
				overlay = null;
			}
		}
	}
	internal enum ConfigOrigin
	{
		Startup,
		LocalFile,
		ServerSync,
		Api
	}
	internal enum ConfigFailurePolicy
	{
		KeepLastGood,
		RevertToDefaults,
		RestoreFileOnDisk
	}
	internal enum ConfigSyncMode
	{
		ServerAuthoritative,
		LocalOnly
	}
	internal enum UnknownKeyPolicy
	{
		WarnAndContinue,
		Strict,
		Silent
	}
	internal abstract class YamlConfigFile
	{
		internal string FileName { get; set; }

		internal string SubFolder { get; set; }

		internal string Header { get; set; }

		internal string RpcName { get; set; }

		internal YamlFormat Format { get; set; }

		internal ConfigFailurePolicy OnFailure { get; set; }

		internal ConfigSyncMode Sync { get; set; }

		internal UnknownKeyPolicy UnknownKeys { get; set; }

		internal bool ClientWritesToDisk { get; set; }

		internal bool Watch { get; set; } = true;

		internal bool NeedsPrefabs { get; set; }

		internal int SchemaVersion { get; set; }

		internal bool AllowAdminEdit { get; set; }

		internal string Path { get; set; }

		internal CustomRPC Rpc { get; set; }

		internal CustomRPC EditRpc { get; set; }

		internal bool LastLoadFailed { get; set; }

		internal string LastError { get; set; }

		internal DateTime LastLoadedUtc { get; set; }

		internal ValidationReport LastReport { get; set; }

		internal YamlFormat EffectiveFormat => Format ?? YamlFormat.Default;

		internal abstract Type ValueType { get; }

		internal abstract string SerializeDefaults();

		internal abstract string SerializeCurrent();

		internal abstract bool LoadFrom(string yaml, ConfigOrigin origin);

		internal abstract ValidationReport Revalidate();

		internal abstract ValidationReport DryRun(string yaml, out string parseError);
	}
	internal sealed class YamlConfigFile<T> : YamlConfigFile where T : class
	{
		internal Func<T> Defaults { get; set; }

		internal Action<T> Apply { get; set; }

		internal Func<T, T, ValidationReport> Validate { get; set; }

		internal Func<T, T> Migrate { get; set; }

		internal Func<T, int> GetSchemaVersion { get; set; }

		internal Action<T, int> SetSchemaVersion { get; set; }

		internal Func<T, bool> MigrateInPlace { get; set; }

		internal T Value { get; private set; }

		internal override Type ValueType => typeof(T);

		internal YamlConfigFile(string fileName)
		{
			base.FileName = fileName;
		}

		internal override string SerializeDefaults()
		{
			T val = BuildDefaults();
			if (val != null)
			{
				return base.EffectiveFormat.Serializer.Serialize((object)val);
			}
			return "";
		}

		internal override string SerializeCurrent()
		{
			if (Value != null)
			{
				return base.EffectiveFormat.Serializer.Serialize((object)Value);
			}
			return SerializeDefaults();
		}

		internal override ValidationReport DryRun(string yaml, out string parseError)
		{
			parseError = null;
			if (YamlConfigManager.HasNoUsableConfig(yaml))
			{
				parseError = "it is empty or contains only comments";
				return new ValidationReport();
			}
			string reason;
			T val = Deserialize(yaml, out reason);
			if (val == null)
			{
				parseError = reason ?? "it could not be parsed";
				return new ValidationReport();
			}
			if (MigrateInPlace != null)
			{
				try
				{
					MigrateInPlace(val);
				}
				catch (Exception ex)
				{
					Logger.LogWarning(base.FileName + " migration threw during a dry run: " + ex.Message);
				}
			}
			if (Validate == null)
			{
				return new ValidationReport();
			}
			try
			{
				return Validate(val, Value) ?? new ValidationReport();
			}
			catch (Exception ex2)
			{
				return new ValidationReport().Error("the validator threw: " + ex2.Message);
			}
		}

		internal override ValidationReport Revalidate()
		{
			if (Validate == null || Value == null)
			{
				return new ValidationReport();
			}
			ValidationReport validationReport;
			try
			{
				validationReport = Validate(Value, Value) ?? new ValidationReport();
			}
			catch (Exception ex)
			{
				validationReport = new ValidationReport().Error("the validator threw: " + ex.Message);
			}
			base.LastReport = validationReport;
			LogReport(validationReport);
			return validationReport;
		}

		internal override bool LoadFrom(string yaml, ConfigOrigin origin)
		{
			T parsed = null;
			string reason;
			if (YamlConfigManager.HasNoUsableConfig(yaml))
			{
				reason = "it is empty or contains only comments";
			}
			else
			{
				parsed = Deserialize(yaml, out reason);
			}
			if (parsed == null)
			{
				return Fail(reason, origin);
			}
			bool changed = false;
			if (base.SchemaVersion > 0 && GetSchemaVersion != null && !ApplySchemaVersion(ref parsed, out var problem, out changed))
			{
				return Fail(problem, origin);
			}
			if (MigrateInPlace != null)
			{
				try
				{
					if (MigrateInPlace(parsed))
					{
						changed = true;
					}
				}
				catch (Exception ex)
				{
					Logger.LogWarning(base.FileName + " migration threw, the file was left as it is: " + ex.Message);
				}
			}
			ValidationReport validationReport = new ValidationReport();
			if (Validate != null)
			{
				try
				{
					validationReport = Validate(parsed, Value) ?? new ValidationReport();
				}
				catch (Exception ex2)
				{
					validationReport = new ValidationReport().Error("the validator threw: " + ex2.Message);
				}
			}
			base.LastReport = validationReport;
			LogReport(validationReport);
			if (validationReport.HasErrors)
			{
				return Fail(string.Join(" ", validationReport.Errors.ToArray()), origin);
			}
			Value = parsed;
			base.LastLoadFailed = false;
			base.LastError = null;
			base.LastLoadedUtc = DateTime.UtcNow;
			Publish();
			if (changed && origin != ConfigOrigin.ServerSync && ((Object)(object)ZNet.instance == (Object)null || ZNet.instance.IsServer()))
			{
				Logger.LogInfo(base.FileName + " was migrated to the current format; rewriting it.");
				YamlConfigManager.WriteCurrentToDisk(this);
			}
			return true;
		}

		private T Deserialize(string yaml, out string reason)
		{
			//IL_00a6: Expected O, but got Unknown
			//IL_002c: Expected O, but got Unknown
			reason = null;
			YamlFormat effectiveFormat = base.EffectiveFormat;
			try
			{
				T val = effectiveFormat.Deserializer.Deserialize<T>(yaml);
				if (val == null)
				{
					reason = "it is empty or contains only comments";
				}
				return val;
			}
			catch (YamlException ex)
			{
				YamlException e = ex;
				if (base.UnknownKeys == UnknownKeyPolicy.Strict)
				{
					reason = Describe(e);
					return null;
				}
				try
				{
					T val2 = effectiveFormat.TolerantDeserializer.Deserialize<T>(yaml);
					if (val2 == null)
					{
						reason = "it is empty or contains only comments";
						return null;
					}
					if (base.UnknownKeys == UnknownKeyPolicy.WarnAndContinue)
					{
						Logger.LogWarning(base.FileName + " " + Describe(e) + " That setting was ignored; the rest of the file loaded normally.");
					}
					return val2;
				}
				catch (YamlException ex2)
				{
					YamlException e2 = ex2;
					reason = Describe(e2);
					return null;
				}
			}
			catch (Exception ex3)
			{
				reason = ex3.Message;
				return null;
			}
		}

		private bool ApplySchemaVersion(ref T parsed, out string problem, out bool changed)
		{
			problem = null;
			changed = false;
			int num;
			try
			{
				num = GetSchemaVersion(parsed);
			}
			catch (Exception ex)
			{
				problem = "the schema version could not be read: " + ex.Message;
				return false;
			}
			if (num == base.SchemaVersion)
			{
				return true;
			}
			if (Migrate == null)
			{
				problem = $"it is schema version {num} but this mod expects {base.SchemaVersion}, and there is no migration for it";
				return false;
			}
			T val;
			try
			{
				val = Migrate(parsed);
			}
			catch (Exception ex2)
			{
				problem = $"migrating from schema version {num} to {base.SchemaVersion} threw: {ex2.Message}";
				return false;
			}
			if (val == null)
			{
				problem = $"migrating from schema version {num} to {base.SchemaVersion} produced nothing";
				return false;
			}
			parsed = val;
			SetSchemaVersion?.Invoke(parsed, base.SchemaVersion);
			changed = true;
			Logger.LogInfo($"{base.FileName} migrated from schema version {num} to {base.SchemaVersion}.");
			return true;
		}

		private bool Fail(string reason, ConfigOrigin origin)
		{
			base.LastLoadFailed = true;
			base.LastError = reason;
			switch (base.OnFailure)
			{
			case ConfigFailurePolicy.RevertToDefaults:
				PublishDefaults();
				Logger.LogError(base.FileName + " could not be loaded because " + reason + ". This mod's built-in defaults are in use; your file was left alone.");
				break;
			case ConfigFailurePolicy.RestoreFileOnDisk:
				PublishDefaults();
				if (origin != ConfigOrigin.ServerSync && ((Object)(object)ZNet.instance == (Object)null || ZNet.instance.IsServer()))
				{
					YamlConfigManager.RestoreDefaults(this);
					Logger.LogError(base.FileName + " could not be loaded because " + reason + ". It has been overwritten with this mod's built-in defaults.");
				}
				else
				{
					Logger.LogError(base.FileName + " could not be loaded because " + reason + ". This mod's built-in defaults are in use; the file was left alone because this machine does not own it.");
				}
				break;
			default:
				if (Value == null)
				{
					PublishDefaults();
					Logger.LogError(base.FileName + " could not be loaded because " + reason + ". Nothing had loaded successfully yet, so this mod's built-in defaults are in use; your file was left alone.");
				}
				else
				{
					Logger.LogError(base.FileName + " could not be loaded because " + reason + ". The values that last loaded cleanly are still in use; your file was left alone.");
				}
				break;
			}
			return false;
		}

		private void PublishDefaults()
		{
			T val = BuildDefaults();
			if (val != null)
			{
				Value = val;
				Publish();
			}
		}

		private void Publish()
		{
			try
			{
				Apply?.Invoke(Value);
			}
			catch (Exception arg)
			{
				Logger.LogError($"{base.FileName} apply hook threw, the mod may be in a half-configured state: {arg}");
			}
		}

		private T BuildDefaults()
		{
			if (Defaults == null)
			{
				return null;
			}
			try
			{
				return Defaults();
			}
			catch (Exception arg)
			{
				Logger.LogError($"{base.FileName} default factory threw: {arg}");
				return null;
			}
		}

		private void LogReport(ValidationReport report)
		{
			if (report != null)
			{
				for (int i = 0; i < report.Warnings.Count; i++)
				{
					Logger.LogWarning(base.FileName + ": " + report.Warnings[i]);
				}
			}
		}

		private static string Describe(YamlException e)
		{
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			string arg = ((((Exception)(object)e).InnerException != null) ? ((Exception)(object)e).InnerException.Message : ((Exception)(object)e).Message);
			Mark start = e.Start;
			return $"line {((Mark)(ref start)).Line}: {arg}";
		}
	}
	internal sealed class YamlFormat
	{
		private readonly Action<SerializerBuilder> configureSerializer;

		private readonly Action<DeserializerBuilder> configureDeserializer;

		private static readonly List<YamlFormat> built;

		private static readonly List<IYamlTypeConverter> converters;

		internal ISerializer Serializer { get; private set; }

		internal IDeserializer Deserializer { get; private set; }

		internal IDeserializer TolerantDeserializer { get; private set; }

		internal static YamlFormat Default { get; private set; }

		internal static YamlFormat CamelCase { get; private set; }

		internal static YamlFormat JsonCompat { get; private set; }

		static YamlFormat()
		{
			built = new List<YamlFormat>();
			converters = new List<IYamlTypeConverter>();
			Default = Build(delegate(SerializerBuilder s)
			{
				((BuilderSkeleton<SerializerBuilder>)(object)s).WithNamingConvention(PascalCaseNamingConvention.Instance);
			}, null);
			CamelCase = Build(delegate(SerializerBuilder s)
			{
				((BuilderSkeleton<SerializerBuilder>)(object)s).WithNamingConvention(CamelCaseNamingConvention.Instance);
			}, null);
			JsonCompat = Build(delegate(SerializerBuilder s)
			{
				((BuilderSkeleton<SerializerBuilder>)(object)s).WithNamingConvention(PascalCaseNamingConvention.Instance).JsonCompatible();
			}, null);
		}

		private YamlFormat(Action<SerializerBuilder> serializerSetup, Action<DeserializerBuilder> deserializerSetup)
		{
			configureSerializer = serializerSetup;
			configureDeserializer = deserializerSetup;
			Rebuild();
		}

		internal static YamlFormat Build(Action<SerializerBuilder> configureSerializer, Action<DeserializerBuilder> configureDeserializer)
		{
			YamlFormat yamlFormat = new YamlFormat(configureSerializer, configureDeserializer);
			built.Add(yamlFormat);
			return yamlFormat;
		}

		internal static void AddTypeConverter(IYamlTypeConverter converter)
		{
			if (converter != null)
			{
				converters.Add(converter);
				for (int i = 0; i < built.Count; i++)
				{
					built[i].Rebuild();
				}
			}
		}

		private void Rebuild()
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			SerializerBuilder val = new SerializerBuilder().DisableAliases().ConfigureDefaultValuesHandling((DefaultValuesHandling)2);
			configureSerializer?.Invoke(val);
			for (int i = 0; i < converters.Count; i++)
			{
				((BuilderSkeleton<SerializerBuilder>)(object)val).WithTypeConverter(converters[i]);
			}
			Serializer = val.Build();
			Deserializer = BuildDeserializer(tolerant: false);
			TolerantDeserializer = BuildDeserializer(tolerant: true);
		}

		private IDeserializer BuildDeserializer(bool tolerant)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			DeserializerBuilder val = new DeserializerBuilder().WithCaseInsensitivePropertyMatching();
			if (tolerant)
			{
				val.IgnoreUnmatchedProperties();
			}
			configureDeserializer?.Invoke(val);
			for (int i = 0; i < converters.Count; i++)
			{
				((BuilderSkeleton<DeserializerBuilder>)(object)val).WithTypeConverter(converters[i]);
			}
			return val.Build();
		}
	}
	internal class Logger
	{
		public static LogLevel Level = (LogLevel)16;

		public static void EnableDebugLogging(object sender, EventArgs e)
		{
			CheckEnableDebugLogging();
		}

		public static void CheckEnableDebugLogging()
		{
			//IL_0016: 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)
			if (ValConfig.EnableDebugMode.Value)
			{
				Level = (LogLevel)32;
			}
			else
			{
				Level = (LogLevel)16;
			}
		}

		public static void SetDebugLogging(bool state)
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			if (state)
			{
				Level = (LogLevel)32;
			}
			else
			{
				Level = (LogLevel)16;
			}
		}

		public static void LogDebug(string message)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Invalid comparison between Unknown and I4
			if ((int)Level >= 32)
			{
				CommunityPatchExtras.Log.LogInfo((object)("[DEBUG]" + message));
			}
		}

		public static void LogInfo(string message)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Invalid comparison between Unknown and I4
			if ((int)Level >= 16)
			{
				CommunityPatchExtras.Log.LogInfo((object)message);
			}
		}

		public static void LogWarning(string message)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Invalid comparison between Unknown and I4
			if ((int)Level >= 4)
			{
				CommunityPatchExtras.Log.LogWarning((object)message);
			}
		}

		public static void LogError(string message)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Invalid comparison between Unknown and I4
			if ((int)Level >= 2)
			{
				CommunityPatchExtras.Log.LogError((object)message);
			}
		}
	}
	internal static class TerminalManager
	{
		internal const string CommandPrefix = "jms";

		internal static readonly Dictionary<string, ModCommand> Registry = new Dictionary<string, ModCommand>();

		private static Terminal responseTerminal;

		private static bool initialized;

		private static Harmony harmony;

		private static void RegisterCommands()
		{
			RegisterHelpCommand();
			RegisterExampleCommands();
		}

		private static void RegisterExampleCommands()
		{
			new ModCommand("jms-echo", "Format: [info/detail/warning/error] [message] Prints a message back at the given severity.", Echo, "demo", EchoOptions, false, false, false, false, null, "jms-e");
			new ModCommand("jms-nearby", "Format: [optional: radius] Counts the creatures around you.", Nearby, "demo", TerminalArgs.RadiusPresets, false, false, false, false, null);
			new ModCommand("jms-worldinfo", "Reports server-side world state. Admins can run this from a connected client.", WorldInfo, "world", null, false, true, true, false, null);
		}

		private static List<string> EchoOptions(string[] input)
		{
			if (input.Length > 2)
			{
				return new List<string>();
			}
			return TerminalArgs.Names<OutputLevel>();
		}

		private static void Echo(ModCommandArgs args)
		{
			if (args.Length < 2)
			{
				args.Output.Error("Usage: jms-echo [info/detail/warning/error] [message]");
				return;
			}
			OutputLevel level = args.Args.GetEnum(0, OutputLevel.Info);
			args.Output.Write(level, args.Args.GetStringFrom(1));
		}

		private static void Nearby(ModCommandArgs args)
		{
			if (!args.HasCenter)
			{
				args.Output.Error("This needs a player position to measure from.");
				return;
			}
			float radius = args.ReadRadius(0, 32f, 256f);
			int num = Character.GetAllCharacters().Count((Character c) => (Object)(object)c != (Object)null && !c.IsPlayer() && Utils.DistanceXZ(((Component)c).transform.position, args.Center) <= radius);
			args.Output.Info($"{num} creatures within {radius}m.");
		}

		private static void WorldInfo(ModCommandArgs args)
		{
			args.Output.Info("World      : " + ZNet.instance.GetWorldName());
			args.Output.Detail("Seed       : " + ZNet.m_world?.m_seedName);
			args.Output.Detail($"Peers      : {ZNet.instance.GetPeers().Count}");
			args.Output.Detail($"ZDOs       : {((ZDOMan.instance != null) ? ZDOMan.instance.NrOfObjects() : 0)}");
			args.Output.Detail($"Zones      : {(((Object)(object)ZoneSystem.instance != (Object)null) ? ZoneSystem.instance.m_generatedZones.Count : 0)}");
			if (args.HasCenter)
			{
				args.Output.Detail($"You are at : {args.Center.x:0}, {args.Center.z:0}");
			}
		}

		private static void RegisterHelpCommand()
		{
			new ModCommand("jms-help", "Format: [optional: area] Lists this mod's console commands, optionally just one area.", Help, "meta", HelpOptions, false, false, false, false, null);
		}

		private static List<string> HelpOptions(string[] input)
		{
			if (input.Length > 2)
			{
				return new List<string>();
			}
			return (from area in (from command in Registry.Values
					where !command.HideFromHelp
					select command.Area).Distinct()
				orderby area
				select area).ToList();
		}

		private static void Help(ModCommandArgs args)
		{
			string filter = args.Args.GetString(0, null);
			if (filter != null && !HelpOptions(new string[2] { "", "" }).Contains(filter))
			{
				args.Output.Warning("Unknown area '" + filter + "'. Areas: " + string.Join(", ", HelpOptions(new string[2] { "", "" })));
				return;
			}
			args.Output.Info("ValheimCommunityPatchExtras commands:", log: false);
			foreach (IGrouping<string, ModCommand> item in from command in Registry.Values
				where !command.HideFromHelp
				where filter == null || command.Area == filter
				group command by command.Area into @group
				orderby @group.Key
				select @group)
			{
				args.Output.Info("  [" + item.Key + "]", log: false);
				foreach (ModCommand item2 in item.OrderBy((ModCommand command) => ((ConsoleCommand)command).Command))
				{
					args.Output.Detail("    " + ((ConsoleCommand)item2).Command + Tags(item2) + " - " + ((ConsoleCommand)item2).Description, log: false);
					string text = AliasesOf(item2);
					if (text.Length > 0)
					{
						args.Output.Detail("      also accepts: " + text, log: false);
					}
				}
			}
		}

		private static string Tags(ModCommand command)
		{
			List<string> list = new List<string>();
			if (((ConsoleCommand)command).IsCheat)
			{
				list.Add("cheat");
			}
			if (command.RequiresAdmin)
			{
				list.Add("admin");
			}
			if (command.ServerAuthoritative)
			{
				list.Add("server");
			}
			if (list.Count != 0)
			{
				return " (" + string.Join(", ", list) + ")";
			}
			return string.Empty;
		}

		private static string AliasesOf(ModCommand command)
		{
			return string.Join(", ", from other in Registry.Values
				where other.HideFromHelp && other.Canonical == ((ConsoleCommand)command).Command
				select ((ConsoleCommand)other).Command into name
				orderby name
				select name);
		}

		internal static void Init()
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Expected O, but got Unknown
			if (!initialized)
			{
				initialized = true;
				harmony = new Harmony("MidnightsFX.ValheimCommunityPatchExtras.terminal");
				PatchTabCompletion("tabCycle");
				PatchTabCompletion("updateSearch");
				TerminalNetwork.Init();
				RegisterCommands();
				Logger.LogDebug($"Registered {Registry.Count} console commands.");
			}
		}

		private static void PatchTabCompletion(string methodName)
		{
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Expected O, but got Unknown
			try
			{
				MethodInfo methodInfo = AccessTools.Method(typeof(Terminal), methodName, (Type[])null, (Type[])null);
				if (methodInfo == null)
				{
					Logger.LogWarning("Terminal." + methodName + " not found; per-argument tab completion is disabled.");
				}
				else
				{
					harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(AccessTools.Method(typeof(TerminalManager), "ContextOptionsPrefix", (Type[])null, (Type[])null)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				}
			}
			catch (Exception ex)
			{
				Logger.LogWarning("Could not patch Terminal." + methodName + " for tab completion: " + ex.Message);
			}
		}

		internal static void Register(ModCommand command)
		{
			Registry[Key(((ConsoleCommand)command).Command)] = command;
		}

		private static string Key(string name)
		{
			return (name ?? string.Empty).ToLowerInvariant();
		}

		private static ModCommand Lookup(string name)
		{
			if (!Registry.TryGetValue(Key(name), out var value))
			{
				return null;
			}
			return value;
		}

		internal static void Execute(string name, ConsoleEventArgs consoleArgs)
		{
			ModCommand modCommand = Lookup(name);
			if (modCommand == null)
			{
				return;
			}
			string[] args = consoleArgs.Args.Skip(1).ToArray();
			TerminalOutput terminalOutput = TerminalOutput.Local(consoleArgs.Context);
			if (!SynchronizationManager.Instance.PlayerIsAdmin)
			{
				terminalOutput.Error("Only server admins can run " + modCommand.Canonical + ".");
				return;
			}
			if (!modCommand.ServerAuthoritative)
			{
				Invoke(modCommand, args, terminalOutput);
				return;
			}
			if ((Object)(object)ZNet.instance == (Object)null)
			{
				terminalOutput.Error("You must be in a world to use " + modCommand.Canonical + ".");
				return;
			}
			if (ZNet.instance.IsServer())
			{
				Invoke(modCommand, args, terminalOutput);
				return;
			}
			if (modCommand.RequiresAdmin && !SynchronizationManager.Instance.PlayerIsAdmin)
			{
				terminalOutput.Error("Only server admins can run " + modCommand.Canonical + " from a client.");
				return;
			}
			ZNetPeer serverPeer = ZNet.instance.GetServerPeer();
			if (serverPeer == null)
			{
				terminalOutput.Error("No server connection, so " + modCommand.Canonical + " cannot be sent.");
				return;
			}
			responseTerminal = consoleArgs.Context;
			terminalOutput.Info("Asked the server to run " + modCommand.Canonical + "; its output follows.");
			TerminalNetwork.SendRequest(serverPeer.m_uid, modCommand.Canonical, args);
		}

		internal static void ExecuteFromNetwork(string name, string[] args, Vector3 center, bool hasCenter, TerminalOutput output)
		{
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			ModCommand modCommand = Lookup(name);
			if (modCommand == null || !modCommand.ServerAuthoritative)
			{
				output.Error("'" + name + "' is not a server-runnable command.");
				output.Flush();
			}
			else
			{
				Invoke(modCommand, args, center, hasCenter, output);
			}
		}

		private static void Invoke(ModCommand command, string[] args, TerminalOutput output)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: 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_001f: 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)
			Vector3 center = Vector3.zero;
			bool hasCenter = false;
			if ((Object)(object)Player.m_localPlayer != (Object)null)
			{
				center = ((Component)Player.m_localPlayer).transform.position;
				hasCenter = true;
			}
			Invoke(command, args, center, hasCenter, output);
		}

		private static void Invoke(ModCommand command, string[] args, Vector3 center, bool hasCenter, TerminalOutput output)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				command.Action(new ModCommandArgs(args, center, hasCenter, output));
			}
			catch (Exception ex)
			{
				output.Error(command.Canonical + " failed: " + ex.Message);
				Logger.LogError($"{command.Canonical} threw: {ex}");
			}
			finally
			{
				output.Flush();
			}
		}

		internal static void PrintResponse(OutputLevel level, string line)
		{
			TerminalOutput.LogLine(level, line);
			Terminal val = (((Object)(object)responseTerminal != (Object)null) ? responseTerminal : null);
			if ((Object)(object)val == (Object)null && (Object)(object)Console.instance != (Object)null)
			{
				val = (Terminal)(object)Console.instance;
			}
			TerminalOutput.PrintTo(val, level, line);
		}

		private static void ContextOptionsPrefix(Terminal __instance, ref string word, ref List<string> options, bool usePrefix)
		{
			if (usePrefix || (Object)(object)__instance == (Object)null || (Object)(object)__instance.m_input == (Object)null)
			{
				return;
			}
			string[] array = (((TMP_InputField)__instance.m_input).text ?? string.Empty).Split(new char[1] { ' ' });
			if (array.Length < 2)
			{
				return;
			}
			ModCommand modCommand = Lookup((__instance.m_tabPrefix == '\0') ? array[0] : ((array[0].Length == 0) ? string.Empty : array[0].Substring(1)));
			if (modCommand != null)
			{
				List<string> tabOptions = modCommand.GetTabOptions(array);
				if (tabOptions != null && tabOptions.Count != 0)
				{
					options = tabOptions;
					word = array[^1];
				}
			}
		}
	}
	internal enum OutputLevel : byte
	{
		Info,
		Detail,
		Warning,
		Error
	}
	internal delegate List<string> OptionProvider(string[] input);
	internal delegate void CommandAction(ModCommandArgs args);
	internal class ModCommand : ConsoleCommand
	{
		internal readonly string Canonical;

		internal readonly string Area;

		internal readonly CommandAction Action;

		internal readonly OptionProvider Options;

		internal readonly bool HideFromHelp;

		internal readonly bool ServerAuthoritative;

		internal readonly bool RequiresAdmin;

		internal ModCommand(string command, string description, CommandAction action, string area = "general", OptionProvider options = null, bool isCheat = false, bool serverAuthoritative = false, bool requiresAdmin = false, bool hideFromHelp = false, string canonical = null, params string[] aliases)
			: base(command, description, (ConsoleEvent)delegate(ConsoleEventArgs args)
			{
				TerminalManager.Execute(command, args);
			}, isCheat, false, false, hideFromHelp, false, false, (ConsoleOptionsFetcher)null, false, false, false)
		{
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Expected O, but got Unknown
			//IL_0092: Unknown result type (might be due to invalid IL or missing references)
			//IL_009c: Expected O, but got Unknown
			ModCommand modCommand = this;
			Canonical = canonical ?? command;
			Area = (string.IsNullOrEmpty(area) ? "general" : area);
			Action = action;
			Options = options;
			HideFromHelp = hideFromHelp;
			ServerAuthoritative = serverAuthoritative;
			RequiresAdmin = requiresAdmin;
			base.m_tabOptionsFetcher = (ConsoleOptionsFetcher)(() => modCommand.GetTabOptions(CurrentInput()));
			base.m_alwaysRefreshTabOptions = true;
			TerminalManager.Register(this);
			for (int num = 0; num < aliases.Length; num++)
			{
				new ModCommand(aliases[num], description, action, area, options, isCheat, serverAuthoritative, requiresAdmin, true, Canonical);
			}
		}

		internal List<string> GetTabOptions(string[] input)
		{
			if (Options == null)
			{
				return new List<string>();
			}
			try
			{
				return Options(input) ?? new List<string>();
			}
			catch (Exception ex)
			{
				Logger.LogDebug("Tab options for " + base.Command + " failed: " + ex.Message);
				return new List<string>();
			}
		}

		private static string[] CurrentInput()
		{
			Terminal instance = (Terminal)(object)Console.instance;
			return ((((Object)(object)instance?.m_input == (Object)null) ? string.Empty : ((TMP_InputField)instance.m_input).text) ?? string.Empty).Split(new char[1] { ' ' });
		}
	}
	internal class ModCommandArgs
	{
		internal readonly string[] Args;

		internal readonly TerminalOutput Output;

		internal readonly Vector3 Center;

		internal readonly bool HasCenter;

		internal int Length => Args.Length;

		internal ModCommandArgs(string[] args, Vector3 center, bool hasCenter, TerminalOutput output)
		{
			//IL_0018: 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)
			Args = args ?? new string[0];
			Center = center;
			HasCenter = hasCenter;
			Output = output;
		}

		internal bool Has(string flag)
		{
			return Args.Any((string a) => string.Equals(a, flag, StringComparison.OrdinalIgnoreCase));
		}
	}
	internal static class TerminalArgs
	{
		private const float CacheSeconds = 60f;

		private static readonly List<string> creatureNames = new List<string>();

		private static float creatureNamesAt;

		internal static string GetString(this string[] args, int index, string fallback = "")
		{
			if (args == null || args.Length <= index)
			{
				return fallback;
			}
			return args[index];
		}

		internal static string GetStringFrom(this string[] args, int index, string fallback = "")
		{
			if (args == null || args.Length <= index)
			{
				return fallback;
			}
			return string.Join(" ", args.Skip(index));
		}

		internal static int GetInt(this string[] args, int index, int fallback = 0)
		{
			string text = args.GetString(index, null);
			if (text == null)
			{
				return fallback;
			}
			if (!int.TryParse(text, out