Decompiled source of EliteBuildingPieces v0.1.0

plugins\EliteBuildingPieces.dll

Decompiled a day ago
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Serialization;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using BundlePrefabs;
using Charter;
using ConfigReload;
using EliteBuildingPieces.Core;
using EliteBuildingPieces.CoreWood;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using PatchGuard;
using SyncedConfig;
using UnityEngine;
using YamlConfig;
using YamlDotNet.Core;
using YamlDotNet.Core.Events;
using YamlDotNet.Core.Tokens;
using YamlDotNet.Helpers;
using YamlDotNet.Serialization;
using YamlDotNet.Serialization.BufferedDeserialization;
using YamlDotNet.Serialization.BufferedDeserialization.TypeDiscriminators;
using YamlDotNet.Serialization.Callbacks;
using YamlDotNet.Serialization.Converters;
using YamlDotNet.Serialization.EventEmitters;
using YamlDotNet.Serialization.NamingConventions;
using YamlDotNet.Serialization.NodeDeserializers;
using YamlDotNet.Serialization.NodeTypeResolvers;
using YamlDotNet.Serialization.ObjectFactories;
using YamlDotNet.Serialization.ObjectGraphTraversalStrategies;
using YamlDotNet.Serialization.ObjectGraphVisitors;
using YamlDotNet.Serialization.Schemas;
using YamlDotNet.Serialization.TypeInspectors;
using YamlDotNet.Serialization.TypeResolvers;
using YamlDotNet.Serialization.Utilities;
using YamlDotNet.Serialization.ValueDeserializers;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: IgnoresAccessChecksTo("assembly_guiutils")]
[assembly: IgnoresAccessChecksTo("assembly_utils")]
[assembly: IgnoresAccessChecksTo("assembly_valheim")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyCompany("EliteBuildingPieces")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("EliteBuildingPieces: new building pieces, starting with core wood walls and doors")]
[assembly: AssemblyFileVersion("0.1.0.0")]
[assembly: AssemblyInformationalVersion("0.1.0+0cf3be3418919774cf3d3c90f9043bcbb4d8d38a")]
[assembly: AssemblyProduct("EliteBuildingPieces")]
[assembly: AssemblyTitle("EliteBuildingPieces")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.1.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace EliteBuildingPieces
{
	[BepInPlugin("milkyteam.elitebuildingpieces", "EliteBuildingPieces", "0.1.0")]
	public class Plugin : BaseUnityPlugin
	{
		public const string PluginGuid = "milkyteam.elitebuildingpieces";

		public const string PluginName = "EliteBuildingPieces";

		public const string PluginVersion = "0.1.0";

		public static ManualLogSource Log { get; private set; }

		public static SyncedConfiguration Synced { get; private set; }

		public static ConfigEntry<bool> LockConfiguration { get; private set; }

		private void Awake()
		{
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			Synced = new SyncedConfiguration((BaseUnityPlugin)(object)this, ((BaseUnityPlugin)this).Logger, "EliteBuildingPieces", "0.1.0");
			LockConfiguration = Synced.BindLocking("General", "Lock Configuration", defaultValue: true, "[Server Only] Clients cannot change synced settings while connected to a server that has this on.");
			CoreWoodModule.Initialize(Synced);
			Harmony harmony = new Harmony("milkyteam.elitebuildingpieces");
			int num = PatchEverything(harmony);
			Synced.Finish(harmony);
			Log.LogInfo((object)("Loading [EliteBuildingPieces 0.1.0]" + ((num > 0) ? $" with {num} failed patches, see above" : "")));
			Guard.Install(harmony, ((BaseUnityPlugin)this).Logger, Assembly.GetExecutingAssembly());
		}

		private static int PatchEverything(Harmony harmony)
		{
			int num = 0;
			Type[] typesFromAssembly = AccessTools.GetTypesFromAssembly(Assembly.GetExecutingAssembly());
			foreach (Type type in typesFromAssembly)
			{
				if (type.GetCustomAttributes(typeof(HarmonyPatch), inherit: true).Length != 0)
				{
					try
					{
						harmony.CreateClassProcessor(type).Patch();
					}
					catch (Exception ex)
					{
						num++;
						Log.LogError((object)("Patch " + type.FullName + " failed to apply: " + ex.Message));
					}
				}
			}
			return num;
		}
	}
}
namespace EliteBuildingPieces.Core
{
	public static class Language
	{
		private static readonly Dictionary<string, string> words = new Dictionary<string, string>();

		public static string Add(string key, string english)
		{
			words[key] = english;
			if (Localization.instance != null)
			{
				Localization.instance.AddWord(key, english);
			}
			return "$" + key;
		}

		internal static void Install(Localization localization)
		{
			foreach (KeyValuePair<string, string> word in words)
			{
				localization.AddWord(word.Key, word.Value);
			}
		}
	}
	[HarmonyPatch(typeof(Localization), "SetupLanguage")]
	public static class LanguageSetupPatch
	{
		[HarmonyPostfix]
		public static void Postfix(Localization __instance)
		{
			Language.Install(__instance);
		}
	}
}
namespace EliteBuildingPieces.CoreWood
{
	[HarmonyPatch]
	public static class CoreWoodHammer
	{
		private const string Hammer = "Hammer";

		private const string CoreWood = "RoundLog";

		[HarmonyPatch(typeof(ObjectDB), "Awake")]
		[HarmonyPostfix]
		[HarmonyPriority(0)]
		private static void DatabaseAwake()
		{
			Refresh();
		}

		[HarmonyPatch(typeof(ZNetScene), "Awake")]
		[HarmonyPostfix]
		private static void SceneAwake()
		{
			Refresh();
		}

		public static void Refresh()
		{
			try
			{
				Apply(ObjectDB.instance);
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)("EliteBuildingPieces: the core wood pieces could not be put in the hammer: " + ex.Message));
			}
		}

		private static void Apply(ObjectDB database)
		{
			PieceTable val = HammerTable(database);
			if ((Object)(object)val == (Object)null)
			{
				return;
			}
			GameObject itemPrefab = database.GetItemPrefab("RoundLog");
			CoreWoodPiece[] all = CoreWoodPieces.All;
			foreach (CoreWoodPiece coreWoodPiece in all)
			{
				if (!((Object)(object)coreWoodPiece.Built == (Object)null))
				{
					val.m_pieces.Remove(coreWoodPiece.Built);
					Configure(coreWoodPiece.Built.GetComponent<Piece>(), coreWoodPiece.Cost, ((Object)(object)itemPrefab != (Object)null) ? itemPrefab.GetComponent<ItemDrop>() : null);
				}
			}
			if (CoreWoodSettings.On)
			{
				Insert(val.m_pieces);
			}
		}

		private static void Configure(Piece piece, int cost, ItemDrop wood)
		{
			//IL_0002: 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)
			//IL_001e: 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_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Expected O, but got Unknown
			piece.m_category = (PieceCategory)2;
			if ((Object)(object)wood != (Object)null)
			{
				piece.m_resources = (Requirement[])(object)new Requirement[1]
				{
					new Requirement
					{
						m_resItem = wood,
						m_amount = cost,
						m_recover = true
					}
				};
			}
		}

		private static void Insert(List<GameObject> pieces)
		{
			int num = pieces.FindIndex((GameObject p) => (Object)(object)p != (Object)null && ((Object)p).name == "wood_gate");
			num = ((num < 0) ? pieces.Count : (num + 1));
			CoreWoodPiece[] all = CoreWoodPieces.All;
			foreach (CoreWoodPiece coreWoodPiece in all)
			{
				if ((Object)(object)coreWoodPiece.Built != (Object)null)
				{
					pieces.Insert(num++, coreWoodPiece.Built);
				}
			}
		}

		private static PieceTable HammerTable(ObjectDB database)
		{
			GameObject val = (((Object)(object)database != (Object)null) ? database.GetItemPrefab("Hammer") : null);
			ItemDrop val2 = (((Object)(object)val != (Object)null) ? val.GetComponent<ItemDrop>() : null);
			if (!((Object)(object)val2 != (Object)null))
			{
				return null;
			}
			return val2.m_itemData.m_shared.m_buildPieces;
		}
	}
	public static class CoreWoodModel
	{
		private const string MaterialSource = "wood_wall_log";

		private const string MaterialName = "logwall";

		private const string Shader = "Custom/Piece";

		private static readonly Dictionary<string, AssetBundle> bundles = new Dictionary<string, AssetBundle>();

		public static bool Wear(GameObject piece, CoreWoodPiece def, Material wood)
		{
			AssetBundle val = Load(def.Bundle);
			GameObject val2 = (((Object)(object)val != (Object)null) ? Model(val, def.Bundle) : null);
			if ((Object)(object)val2 == (Object)null)
			{
				return false;
			}
			Strip(piece);
			GameObject val3 = Object.Instantiate<GameObject>(val2, piece.transform, false);
			((Object)val3).name = "corewood";
			Animator[] componentsInChildren = val3.GetComponentsInChildren<Animator>(true);
			for (int i = 0; i < componentsInChildren.Length; i++)
			{
				Object.DestroyImmediate((Object)(object)componentsInChildren[i]);
			}
			Dress(val3, wood);
			Unmirror(val3);
			SetLayer(val3.transform, piece.layer);
			CoreWoodSnaps.Lift(piece.transform, val3.transform);
			Point(piece.GetComponent<WearNTear>(), val3);
			if (def.IsDoor)
			{
				piece.AddComponent<GateSwing>();
			}
			SetIcon(piece.GetComponent<Piece>(), val);
			return true;
		}

		public static Material LogWall(List<GameObject> prefabs)
		{
			GameObject val = prefabs.Find((GameObject p) => (Object)(object)p != (Object)null && ((Object)p).name == "wood_wall_log");
			if ((Object)(object)val == (Object)null)
			{
				return null;
			}
			MeshRenderer[] componentsInChildren = val.GetComponentsInChildren<MeshRenderer>(true);
			for (int num = 0; num < componentsInChildren.Length; num++)
			{
				Material[] sharedMaterials = ((Renderer)componentsInChildren[num]).sharedMaterials;
				foreach (Material val2 in sharedMaterials)
				{
					if ((Object)(object)val2 != (Object)null && ((Object)val2).name == "logwall" && ((Object)val2.shader).name == "Custom/Piece")
					{
						return val2;
					}
				}
			}
			Plugin.Log.LogWarning((object)"EliteBuildingPieces: wood_wall_log has no logwall: the core wood pieces keep the bundle's materials");
			return null;
		}

		private static AssetBundle Load(string name)
		{
			if (bundles.TryGetValue(name, out var value))
			{
				return value;
			}
			try
			{
				value = EmbeddedBundle.Load(typeof(CoreWoodModel).Assembly, name);
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("EliteBuildingPieces: the model " + name + " did not load: " + ex.Message));
			}
			bundles[name] = value;
			return value;
		}

		private static GameObject Model(AssetBundle bundle, string name)
		{
			GameObject val = bundle.LoadAsset<GameObject>(name);
			if ((Object)(object)val != (Object)null)
			{
				return val;
			}
			GameObject[] array = bundle.LoadAllAssets<GameObject>();
			if (array.Length == 0)
			{
				return null;
			}
			return array[0];
		}

		private static void Strip(GameObject piece)
		{
			for (int num = piece.transform.childCount - 1; num >= 0; num--)
			{
				Object.DestroyImmediate((Object)(object)((Component)piece.transform.GetChild(num)).gameObject);
			}
			Type[] array = new Type[4]
			{
				typeof(LODGroup),
				typeof(Renderer),
				typeof(MeshFilter),
				typeof(Collider)
			};
			foreach (Type type in array)
			{
				Component[] components = piece.GetComponents(type);
				for (int j = 0; j < components.Length; j++)
				{
					Object.DestroyImmediate((Object)(object)components[j]);
				}
			}
		}

		private static void Dress(GameObject placed, Material wood)
		{
			if ((Object)(object)wood == (Object)null)
			{
				return;
			}
			Renderer[] componentsInChildren = placed.GetComponentsInChildren<Renderer>(true);
			foreach (Renderer val in componentsInChildren)
			{
				Material[] sharedMaterials = val.sharedMaterials;
				for (int j = 0; j < sharedMaterials.Length; j++)
				{
					sharedMaterials[j] = wood;
				}
				val.sharedMaterials = sharedMaterials;
			}
		}

		private static void Unmirror(GameObject placed)
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: 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_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: 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_006c: 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)
			BoxCollider[] componentsInChildren = placed.GetComponentsInChildren<BoxCollider>(true);
			Vector3 val2 = default(Vector3);
			foreach (BoxCollider val in componentsInChildren)
			{
				Vector3 localScale = ((Component)val).transform.localScale;
				((Vector3)(ref val2))..ctor(Mathf.Sign(localScale.x), Mathf.Sign(localScale.y), Mathf.Sign(localScale.z));
				if (!(val2 == Vector3.one))
				{
					val.center = Vector3.Scale(val.center, val2);
					((Component)val).transform.localScale = Vector3.Scale(localScale, val2);
				}
			}
		}

		private static void SetLayer(Transform part, int layer)
		{
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Expected O, but got Unknown
			((Component)part).gameObject.layer = layer;
			foreach (Transform item in part)
			{
				SetLayer(item, layer);
			}
		}

		private static void Point(WearNTear wear, GameObject placed)
		{
			if (!((Object)(object)wear == (Object)null))
			{
				wear.m_new = placed;
				wear.m_worn = placed;
				wear.m_broken = placed;
				wear.m_wet = null;
				wear.m_fragmentRoots = null;
			}
		}

		private static void SetIcon(Piece piece, AssetBundle bundle)
		{
			Sprite[] array = bundle.LoadAllAssets<Sprite>();
			if ((Object)(object)piece != (Object)null && array.Length != 0)
			{
				piece.m_icon = array[0];
			}
		}
	}
	public static class CoreWoodSettings
	{
		public static ConfigEntry<bool> Enabled { get; private set; }

		public static bool On
		{
			get
			{
				if (Enabled != null)
				{
					return Enabled.Value;
				}
				return false;
			}
		}

		public static void Initialize(SyncedConfiguration synced)
		{
			Enabled = synced.Bind("1. Core Wood", "Core Wood Pieces", true, "Adds four core wood pieces to the hammer's Building tab, after the wood gate: a wall (4 core wood), a large wall twice as wide (8), a door (6) and a double door (12). The walls snap and hold like the stakewall; the doors open away from you like the wood gate. Off: they leave the hammer, pieces already built stay.", true, null);
		}
	}
	public static class CoreWoodModule
	{
		public static void Initialize(SyncedConfiguration synced)
		{
			CoreWoodSettings.Initialize(synced);
			CoreWoodSettings.Enabled.SettingChanged += delegate
			{
				CoreWoodHammer.Refresh();
			};
			AddWords();
		}

		private static void AddWords()
		{
			Language.Add("ebp_corewood_wall", "Core wood wall");
			Language.Add("ebp_corewood_wall_desc", "Core wood logs laid between two posts. Snaps like the stakewall.");
			Language.Add("ebp_corewood_wall_large", "Large core wood wall");
			Language.Add("ebp_corewood_wall_large_desc", "A core wood wall twice as wide, in one piece.");
			Language.Add("ebp_corewood_door", "Core wood door");
			Language.Add("ebp_corewood_door_desc", "A core wood gate the width of a core wood wall. Opens away from you.");
			Language.Add("ebp_corewood_door_double", "Core wood double door");
			Language.Add("ebp_corewood_door_double_desc", "Two core wood gates that open together, the width of a large core wood wall.");
		}
	}
	public sealed class CoreWoodPiece
	{
		public string Prefab { get; }

		public string Source { get; }

		public string Bundle { get; }

		public string Word { get; }

		public int Cost { get; }

		public int Width { get; }

		public GameObject Built { get; set; }

		public bool IsDoor => Source == "wood_gate";

		public string NameToken => "$ebp_" + Word;

		public string DescriptionToken => "$ebp_" + Word + "_desc";

		public CoreWoodPiece(string prefab, string source, string bundle, string word, int cost, int width)
		{
			Prefab = prefab;
			Source = source;
			Bundle = bundle;
			Word = word;
			Cost = cost;
			Width = width;
		}
	}
	public static class CoreWoodPieces
	{
		public const string Wall = "stake_wall";

		public const string Gate = "wood_gate";

		public static readonly CoreWoodPiece[] All = new CoreWoodPiece[4]
		{
			new CoreWoodPiece("EBP_CoreWoodWall", "stake_wall", "va_corewoodwall_v002", "corewood_wall", 4, 1),
			new CoreWoodPiece("EBP_CoreWoodWallLarge", "stake_wall", "va_corewoodwall_double_v003", "corewood_wall_large", 8, 2),
			new CoreWoodPiece("EBP_CoreWoodDoor", "wood_gate", "va_corewoodgate_single_v002", "corewood_door", 6, 1),
			new CoreWoodPiece("EBP_CoreWoodDoubleDoor", "wood_gate", "va_corewoodgate_double_v002", "corewood_door_double", 12, 2)
		};
	}
	[HarmonyPatch(typeof(ZNetScene), "Awake")]
	public static class CoreWoodPrefabs
	{
		[HarmonyPrefix]
		private static void Prefix(ZNetScene __instance)
		{
			try
			{
				AddTo(__instance.m_prefabs);
			}
			catch (Exception arg)
			{
				Plugin.Log.LogError((object)$"EliteBuildingPieces: the core wood pieces could not be made: {arg}");
			}
		}

		private static void AddTo(List<GameObject> prefabs)
		{
			Material val = null;
			CoreWoodPiece[] all = CoreWoodPieces.All;
			foreach (CoreWoodPiece coreWoodPiece in all)
			{
				if ((Object)(object)coreWoodPiece.Built == (Object)null)
				{
					coreWoodPiece.Built = Build(prefabs, coreWoodPiece, val ?? (val = CoreWoodModel.LogWall(prefabs)));
				}
				if ((Object)(object)coreWoodPiece.Built != (Object)null && !prefabs.Contains(coreWoodPiece.Built))
				{
					prefabs.Add(coreWoodPiece.Built);
				}
			}
		}

		private static GameObject Build(List<GameObject> prefabs, CoreWoodPiece piece, Material wood)
		{
			GameObject val = prefabs.Find((GameObject p) => (Object)(object)p != (Object)null && ((Object)p).name == piece.Source);
			if ((Object)(object)val == (Object)null || (Object)(object)val.GetComponent<Piece>() == (Object)null)
			{
				Plugin.Log.LogWarning((object)("EliteBuildingPieces: the game's " + piece.Source + " was not found: there is no " + piece.Prefab));
				return null;
			}
			GameObject obj = PrefabBench.Copy(val, piece.Prefab);
			if (!CoreWoodModel.Wear(obj, piece, wood))
			{
				Plugin.Log.LogWarning((object)("EliteBuildingPieces: " + piece.Prefab + " keeps the look of the game's " + piece.Source));
			}
			Configure(obj, piece);
			return obj;
		}

		private static void Configure(GameObject copy, CoreWoodPiece piece)
		{
			Piece component = copy.GetComponent<Piece>();
			component.m_name = piece.NameToken;
			component.m_description = piece.DescriptionToken;
			component.m_enabled = true;
			Door component2 = copy.GetComponent<Door>();
			if ((Object)(object)component2 != (Object)null)
			{
				component2.m_name = piece.NameToken;
			}
			WearNTear component3 = copy.GetComponent<WearNTear>();
			if ((Object)(object)component3 != (Object)null)
			{
				component3.m_health *= (float)piece.Width;
			}
		}
	}
	public static class CoreWoodSnaps
	{
		private const string Anchor = "snap_";

		private const string Brace = "col_box_wall_01";

		public static void Lift(Transform root, Transform placed)
		{
			//IL_0021: 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_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: 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_006b: Unknown result type (might be due to invalid IL or missing references)
			int num = 0;
			int num2 = 0;
			foreach (Transform item in Anchors(placed))
			{
				Vector3 val = root.InverseTransformPoint(item.position);
				item.SetParent(root, false);
				item.localPosition = val;
				item.localRotation = Quaternion.identity;
				((Component)item).gameObject.tag = "snappoint";
				((Component)item).gameObject.SetActive(false);
				((Object)item).name = ((val.y < 1f) ? $"$hud_snappoint_bottom {++num}" : $"$hud_snappoint_mid {++num2}");
			}
			Transform val2 = GameMaterials.Find(placed, "col_box_wall_01");
			if ((Object)(object)val2 != (Object)null)
			{
				((Component)val2).gameObject.tag = "leaky";
			}
		}

		private static List<Transform> Anchors(Transform placed)
		{
			List<Transform> list = new List<Transform>();
			Transform[] componentsInChildren = ((Component)placed).GetComponentsInChildren<Transform>(true);
			foreach (Transform val in componentsInChildren)
			{
				if (((Object)val).name.StartsWith("snap_"))
				{
					list.Add(val);
				}
			}
			list.Sort((Transform a, Transform b) => string.CompareOrdinal(((Object)a).name, ((Object)b).name));
			return list;
		}
	}
	public sealed class GateSwing : MonoBehaviour
	{
		private const float OpenAngle = 100f;

		private const float Speed = 142.85715f;

		private Transform[] leaves;

		private float[] turns;

		private float angle;

		private float target;

		private bool shown;

		private void Awake()
		{
			((Behaviour)this).enabled = false;
		}

		public void Follow(int state)
		{
			Find();
			target = (float)Mathf.Clamp(state, -1, 1) * 100f;
			if (!shown)
			{
				shown = true;
				Turn(angle = target);
			}
			((Behaviour)this).enabled = angle != target;
		}

		private void Update()
		{
			angle = Mathf.MoveTowards(angle, target, 142.85715f * Time.deltaTime);
			Turn(angle);
			if (angle == target)
			{
				((Behaviour)this).enabled = false;
			}
		}

		private void Turn(float degrees)
		{
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			for (int i = 0; i < leaves.Length; i++)
			{
				leaves[i].localRotation = Quaternion.Euler(0f, degrees * turns[i], 0f);
			}
		}

		private void Find()
		{
			if (leaves != null)
			{
				return;
			}
			List<Transform> list = new List<Transform>();
			List<float> list2 = new List<float>();
			Transform[] componentsInChildren = ((Component)this).GetComponentsInChildren<Transform>(true);
			foreach (Transform val in componentsInChildren)
			{
				if (((Object)val).name == "door_left" || ((Object)val).name == "door_right")
				{
					list.Add(val);
					list2.Add((((Object)val).name == "door_left") ? (-1f) : 1f);
				}
			}
			leaves = list.ToArray();
			turns = list2.ToArray();
		}
	}
	[HarmonyPatch(typeof(Door), "SetState")]
	public static class GateSwingPatch
	{
		[HarmonyPostfix]
		private static void Postfix(Door __instance, int state)
		{
			GateSwing component = ((Component)__instance).GetComponent<GateSwing>();
			if ((Object)(object)component != (Object)null)
			{
				component.Follow(state);
			}
		}
	}
	[HarmonyPatch(typeof(Player), "IsOverlappingOtherPiece")]
	public static class WallLine
	{
		private const float Course = 0.5f;

		private const float Depth = 0.2f;

		private const float Touch = 0.1f;

		[HarmonyPostfix]
		private static void Postfix(Vector3 p, Quaternion rotation, string pieceName, List<Piece> pieces, ref bool __result)
		{
			//IL_0039: 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)
			float num = (__result ? 0f : Width(pieceName));
			if (num <= 0f || pieces == null)
			{
				return;
			}
			foreach (Piece piece in pieces)
			{
				if ((Object)(object)piece != (Object)null && Inside(p, rotation, num, piece))
				{
					__result = true;
					break;
				}
			}
		}

		private static bool Inside(Vector3 p, Quaternion rotation, float width, Piece other)
		{
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: 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_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: 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_0054: 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_005e: 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_0071: 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)
			float num = Width(((Object)((Component)other).gameObject).name);
			if (num <= 0f || Mathf.Abs(Vector3.Dot(rotation * Vector3.right, ((Component)other).transform.right)) < 0.98f)
			{
				return false;
			}
			Vector3 val = Quaternion.Inverse(rotation) * (((Component)other).transform.position - p);
			if (Mathf.Abs(val.y) < 0.5f && Mathf.Abs(val.z) < 0.2f)
			{
				return Mathf.Abs(val.x) < (width + num) / 2f - 0.1f;
			}
			return false;
		}

		private static float Width(string name)
		{
			int num = name.IndexOf("(Clone)", StringComparison.Ordinal);
			if (num >= 0)
			{
				name = name.Substring(0, num);
			}
			if (name == "stake_wall")
			{
				return 2f;
			}
			CoreWoodPiece[] all = CoreWoodPieces.All;
			foreach (CoreWoodPiece coreWoodPiece in all)
			{
				if (coreWoodPiece.Prefab == name)
				{
					return 2f * (float)coreWoodPiece.Width;
				}
			}
			return 0f;
		}
	}
}
namespace System.Runtime.CompilerServices
{
	[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
	internal sealed class IgnoresAccessChecksToAttribute : Attribute
	{
		public IgnoresAccessChecksToAttribute(string assemblyName)
		{
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

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

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

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
}
namespace SyncedConfig
{
	public sealed class SyncedConfiguration
	{
		public global::Charter.Charter Sync { get; }

		public ConfigFile Config { get; }

		public ManualLogSource Log { get; }

		public YamlFileHub Yaml { get; }

		public YamlEditorWindow YamlEditor { get; }

		public IReadOnlyList<string> SearchPaths { get; }

		public IDisposable? ConfigWatcher { get; private set; }

		public bool IsLocked
		{
			get
			{
				if (Sync.IsBound)
				{
					return !Sync.MayAmend;
				}
				return false;
			}
		}

		public bool IsAdmin => Sync.IsSteward;

		public bool IsAuthor => Sync.IsAuthor;

		public SyncedConfiguration(BaseUnityPlugin plugin, ManualLogSource log, string title, string version, string? oldestAccepted = null, bool mandatory = true)
		{
			Config = plugin.Config;
			ConfigReloader.HoldSaves(Config);
			Log = log;
			string gUID = plugin.Info.Metadata.GUID;
			Sync = new global::Charter.Charter(gUID, title, version, oldestAccepted, mandatory);
			SearchPaths = new string[2]
			{
				Path.GetDirectoryName(Config.ConfigFilePath) ?? Paths.ConfigPath,
				Path.GetDirectoryName(((object)plugin).GetType().Assembly.Location) ?? Paths.PluginPath
			};
			Yaml = new YamlFileHub(title, Log, SearchPaths, Sync);
			YamlEditor = new YamlEditorWindow(Yaml, title + " YAML Editor");
			YamlEditorHost.Add(((Component)plugin).gameObject, YamlEditor);
		}

		public ConfigEntry<T> Bind<T>(string section, string key, T defaultValue, string description, bool synced = true, AcceptableValueBase? acceptableValues = null, params object[] tags)
		{
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Expected O, but got Unknown
			return Bind(section, key, defaultValue, new ConfigDescription(description, acceptableValues, tags), synced);
		}

		public ConfigEntry<T> Bind<T>(string section, string key, T defaultValue, ConfigDescription description, bool synced = true)
		{
			ConfigEntry<T> val = Config.Bind<T>(section, key, defaultValue, description);
			Sync.Clause<T>(val, !synced);
			return val;
		}

		public Clause<T> Add<T>(ConfigEntry<T> entry, bool synced = true)
		{
			return Sync.Clause<T>(entry, !synced);
		}

		public ConfigEntry<bool> BindLocking(string section, string key, bool defaultValue, string description)
		{
			ConfigEntry<bool> val = Config.Bind<bool>(section, key, defaultValue, description);
			Sync.Binding(val);
			return val;
		}

		public YamlFileSet AddYaml(YamlFileSet set)
		{
			return Yaml.Register(set);
		}

		public void Finish(Harmony harmony, int yamlApplyPriority = 400)
		{
			global::Charter.Charter.Install(harmony);
			ConfigWatcher = ConfigReloader.Setup(Config, Log);
			if (Yaml.Sets.Count > 0)
			{
				Yaml.HookGame(harmony, yamlApplyPriority);
			}
		}

		public static Func<byte[]?> EmbeddedResource(Assembly assembly, string resourceName)
		{
			return delegate
			{
				using Stream stream = assembly.GetManifestResourceStream(resourceName);
				if (stream != null)
				{
					using MemoryStream memoryStream = new MemoryStream();
					stream.CopyTo(memoryStream);
					return memoryStream.ToArray();
				}
				return (byte[]?)null;
			};
		}
	}
}
namespace YamlConfig
{
	internal sealed class YamlEditorHost : MonoBehaviour
	{
		private YamlEditorWindow? window;

		private bool laidOut;

		public static YamlEditorHost Add(GameObject on, YamlEditorWindow window)
		{
			YamlEditorHost yamlEditorHost = on.AddComponent<YamlEditorHost>();
			yamlEditorHost.window = window;
			window.Host = yamlEditorHost;
			((Behaviour)yamlEditorHost).enabled = window.IsOpen;
			return yamlEditorHost;
		}

		private void Update()
		{
			window?.Update();
		}

		private void LateUpdate()
		{
			window?.Update();
		}

		private void OnDisable()
		{
			laidOut = false;
		}

		private void OnGUI()
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Invalid comparison between Unknown and I4
			if (laidOut || (int)Event.current.type == 8)
			{
				laidOut = true;
				window?.Draw();
			}
		}
	}
	public sealed class YamlEditorWindow
	{
		private const float ValidationHeight = 100f;

		private readonly YamlFileHub hub;

		private readonly string windowTitle;

		private readonly List<string> paths = new List<string>();

		private readonly List<string> texts = new List<string>();

		private readonly List<bool> expanded = new List<bool>();

		private readonly List<Vector2> scrolls = new List<Vector2>();

		private readonly List<string> errors = new List<string>();

		private readonly List<string> warnings = new List<string>();

		private Vector2 validationScroll;

		private YamlFileSet? set;

		private Texture2D? background;

		private GUIStyle? errorStyle;

		public Func<string, string> Translate { get; set; } = (string caption) => caption;

		public bool IsOpen => set != null;

		internal YamlEditorHost? Host { get; set; }

		private Texture2D Background
		{
			get
			{
				//IL_0011: Unknown result type (might be due to invalid IL or missing references)
				//IL_001b: Expected O, but got Unknown
				//IL_0037: Unknown result type (might be due to invalid IL or missing references)
				if ((Object)(object)background == (Object)null)
				{
					background = new Texture2D(1, 1);
					background.SetPixel(0, 0, new Color(0f, 0f, 0f, 0.9f));
					background.Apply();
				}
				return background;
			}
		}

		private GUIStyle ErrorStyle
		{
			get
			{
				//IL_0015: Unknown result type (might be due to invalid IL or missing references)
				//IL_001a: Unknown result type (might be due to invalid IL or missing references)
				//IL_0020: Unknown result type (might be due to invalid IL or missing references)
				//IL_002a: Unknown result type (might be due to invalid IL or missing references)
				//IL_002c: Expected O, but got Unknown
				//IL_0031: Expected O, but got Unknown
				GUIStyle obj = errorStyle;
				if (obj == null)
				{
					GUIStyle val = new GUIStyle(GUI.skin.label);
					val.normal.textColor = Color.red;
					GUIStyle val2 = val;
					errorStyle = val;
					obj = val2;
				}
				return obj;
			}
		}

		public YamlEditorWindow(YamlFileHub hub, string windowTitle)
		{
			this.hub = hub;
			this.windowTitle = windowTitle;
		}

		public void Open(YamlFileSet set)
		{
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			this.set = set;
			paths.Clear();
			texts.Clear();
			expanded.Clear();
			scrolls.Clear();
			foreach (KeyValuePair<string, string> file in set.Files)
			{
				paths.Add(file.Key);
				texts.Add(file.Value);
				expanded.Add(item: true);
				scrolls.Add(Vector2.zero);
			}
			Validate();
			FollowHost();
		}

		public void Close()
		{
			set = null;
			FollowHost();
		}

		private void FollowHost()
		{
			if ((Object)(object)Host != (Object)null)
			{
				((Behaviour)Host).enabled = IsOpen;
			}
		}

		public void DrawButtons()
		{
			foreach (YamlFileSet item in hub.Sets.Where((YamlFileSet s) => s.Files.Count > 0))
			{
				if (GUILayout.Button(Translate(item.EditorLabel()), (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(true) }))
				{
					Open(item);
				}
			}
		}

		public void Update()
		{
			if (IsOpen)
			{
				Cursor.lockState = (CursorLockMode)0;
				Cursor.visible = true;
			}
		}

		public void OnGUI()
		{
			if ((Object)(object)Host == (Object)null)
			{
				Draw();
			}
		}

		internal void Draw()
		{
			//IL_0025: 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)
			if (set != null)
			{
				HandleEditingKeys();
				GUI.DrawTexture(new Rect(0f, 0f, (float)Screen.width, (float)Screen.height), (Texture)(object)Background);
				GUILayout.BeginArea(new Rect(20f, 20f, (float)(Screen.width - 40), (float)(Screen.height - 40)));
				GUILayout.Label(Translate(windowTitle), Array.Empty<GUILayoutOption>());
				DrawTopRow();
				for (int i = 0; i < texts.Count; i++)
				{
					DrawFile(i);
				}
				DrawValidation();
				GUILayout.EndArea();
			}
		}

		private void DrawTopRow()
		{
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			GUI.enabled = hub.IsAuthor && errors.Count == 0;
			if (GUILayout.Button(Translate("Save and apply"), Array.Empty<GUILayoutOption>()))
			{
				Submit(saveToDisk: true);
			}
			if (GUILayout.Button(Translate("Apply without saving"), Array.Empty<GUILayoutOption>()))
			{
				Submit(saveToDisk: false);
			}
			GUI.enabled = true;
			if (GUILayout.Button(Translate("Discard"), Array.Empty<GUILayoutOption>()))
			{
				Close();
			}
			GUILayout.EndHorizontal();
		}

		private void Submit(bool saveToDisk)
		{
			if (set != null)
			{
				Dictionary<string, string> dictionary = new Dictionary<string, string>();
				for (int i = 0; i < paths.Count; i++)
				{
					dictionary[paths[i]] = texts[i];
				}
				hub.Replace(set, dictionary, saveToDisk);
				Close();
			}
		}

		private void DrawFile(int index)
		{
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			if (texts.Count > 1)
			{
				expanded[index] = GUILayout.Toggle(expanded[index], Path.GetFileName(paths[index]), Array.Empty<GUILayoutOption>());
				if (!expanded[index])
				{
					return;
				}
			}
			scrolls[index] = GUILayout.BeginScrollView(scrolls[index], (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandHeight(true) });
			GUI.SetNextControlName(ControlName(index));
			string text = GUILayout.TextArea(texts[index], (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandHeight(true) });
			GUILayout.EndScrollView();
			if (text != texts[index])
			{
				texts[index] = text;
				Validate();
			}
		}

		private void DrawValidation()
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			validationScroll = GUILayout.BeginScrollView(validationScroll, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(100f) });
			if (errors.Count == 0 && warnings.Count == 0)
			{
				GUILayout.Label(Translate("Configuration is valid."), Array.Empty<GUILayoutOption>());
			}
			foreach (string error in errors)
			{
				GUILayout.Label(error, ErrorStyle, Array.Empty<GUILayoutOption>());
			}
			foreach (string warning in warnings)
			{
				GUILayout.Label(warning, Array.Empty<GUILayoutOption>());
			}
			GUILayout.EndScrollView();
		}

		private void Validate()
		{
			errors.Clear();
			warnings.Clear();
			if (set != null)
			{
				YamlModel yamlModel = set.CreateModel();
				yamlModel.LoadAll(paths.Select((string path, int i) => new KeyValuePair<string, string>(path, texts[i])));
				errors.AddRange(yamlModel.Errors);
				warnings.AddRange(yamlModel.Warnings);
			}
		}

		private void HandleEditingKeys()
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Invalid comparison between Unknown and I4
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Invalid comparison between Unknown and I4
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Invalid comparison between Unknown and I4
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Invalid comparison between Unknown and I4
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Expected O, but got Unknown
			Event current = Event.current;
			if ((int)current.type == 4)
			{
				bool flag = (int)current.keyCode == 9 || current.character == '\t';
				bool flag2 = (int)current.keyCode == 13 || (int)current.keyCode == 271 || current.character == '\n';
				int num = FocusedFile();
				if (num >= 0 && (flag || flag2))
				{
					TextEditor val = (TextEditor)GUIUtility.GetStateObject(typeof(TextEditor), GUIUtility.keyboardControl);
					val.ReplaceSelection(flag ? "  " : ("\n" + IndentationOfLineAt(val.text, val.cursorIndex)));
					texts[num] = val.text;
					current.Use();
					Validate();
				}
			}
		}

		private int FocusedFile()
		{
			string nameOfFocusedControl = GUI.GetNameOfFocusedControl();
			for (int i = 0; i < texts.Count; i++)
			{
				if (nameOfFocusedControl == ControlName(i))
				{
					return i;
				}
			}
			return -1;
		}

		private static string IndentationOfLineAt(string text, int position)
		{
			int num = Math.Max(0, Math.Min(position, text.Length));
			while (num > 0 && text[num - 1] != '\n')
			{
				num--;
			}
			int i;
			for (i = num; i < text.Length && text[i] == ' '; i++)
			{
			}
			return text.Substring(num, i - num);
		}

		private static string ControlName(int index)
		{
			return "YamlEditorWindow.File" + index;
		}
	}
	public sealed class YamlFileHub
	{
		private readonly string modName;

		private readonly ManualLogSource log;

		private readonly global::Charter.Charter? charter;

		private readonly List<YamlFileSet> sets = new List<YamlFileSet>();

		private readonly YamlFileStore store;

		private readonly YamlFileWatcher watcher;

		private bool loaded;

		private (YamlFileSet Set, YamlModel Model)? built;

		public IReadOnlyList<YamlFileSet> Sets => sets;

		public bool IsAuthor => charter?.IsAuthor ?? true;

		public bool IsLocked
		{
			get
			{
				if (charter != null && charter.IsBound)
				{
					return !charter.MayAmend;
				}
				return false;
			}
		}

		public bool SuppressWriteBack { get; set; }

		public Func<bool> CanApply { get; set; } = () => true;

		public event Action<YamlFileSet>? Applied;

		public YamlFileHub(string modName, ManualLogSource log, IEnumerable<string> searchFolders, global::Charter.Charter? charter)
		{
			this.modName = modName;
			this.log = log;
			this.charter = charter;
			store = new YamlFileStore(modName, log, searchFolders);
			watcher = new YamlFileWatcher(store, sets, ReloadFromDisk, log, modName);
		}

		public YamlFileSet Register(YamlFileSet set)
		{
			if (sets.Any((YamlFileSet s) => string.Equals(s.SyncKey, set.SyncKey, StringComparison.OrdinalIgnoreCase)))
			{
				throw new ArgumentException(modName + ": a YAML file set with sync key '" + set.SyncKey + "' is already registered");
			}
			sets.Add(set);
			if (charter != null)
			{
				Article<List<string>> channel = new Article<List<string>>(charter, set.SyncKey, new List<string>());
				set.Channel = channel;
				channel.Changed += delegate
				{
					Receive(set, YamlFileList.Unflatten(channel.Value));
				};
			}
			return set;
		}

		public void LoadAll()
		{
			loaded = true;
			foreach (YamlFileSet set in sets)
			{
				LoadSet(set);
			}
			watcher.Start();
		}

		public void ApplyAll()
		{
			if (!CanApply())
			{
				log.LogDebug((object)(modName + ": YAML apply skipped, the game is not ready"));
				return;
			}
			foreach (YamlFileSet set in sets)
			{
				Apply(set);
			}
		}

		public bool TryBuild(YamlFileSet set, IReadOnlyDictionary<string, string> files, out YamlModel model, string context)
		{
			model = set.CreateModel();
			model.LoadAll(files);
			foreach (string warning in model.Warnings)
			{
				log.LogWarning((object)(modName + ": " + warning));
			}
			if (model.Errors.Count == 0)
			{
				return true;
			}
			log.LogError((object)$"{modName}: {set.MainFileName} ({context}) has {model.Errors.Count} error(s), the previous configuration stays:");
			foreach (string error in model.Errors)
			{
				log.LogError((object)(modName + ": " + error));
			}
			return false;
		}

		public void Replace(YamlFileSet set, IReadOnlyDictionary<string, string> files, bool saveToDisk)
		{
			if (TryBuild(set, files, out YamlModel model, "editor"))
			{
				Publish(set, files, model, saveToDisk);
			}
		}

		public void HookGame(Harmony harmony, int applyPriority)
		{
			YamlGameHooks.Install(this, harmony, applyPriority);
		}

		internal void LoadFromHook()
		{
			if (loaded)
			{
				return;
			}
			try
			{
				LoadAll();
			}
			catch (Exception arg)
			{
				log.LogError((object)$"{modName}: loading YAML files failed: {arg}");
			}
		}

		internal void ApplyFromHook()
		{
			try
			{
				ApplyAll();
			}
			catch (Exception arg)
			{
				log.LogError((object)$"{modName}: applying YAML files failed: {arg}");
			}
		}

		private void LoadSet(YamlFileSet set)
		{
			Dictionary<string, string> dictionary = watcher.ReadOrCreate(set);
			YamlModel model;
			if (dictionary.Count == 0)
			{
				if (set.Enabled())
				{
					log.LogWarning((object)(modName + ": no " + set.FilePattern + " found in " + string.Join(" or ", store.SearchFolders)));
				}
			}
			else if (TryBuild(set, dictionary, out model, "startup"))
			{
				Publish(set, dictionary, model, writeBack: false);
			}
		}

		private void ReloadFromDisk(YamlFileSet set)
		{
			Dictionary<string, string> dictionary = watcher.Read(set);
			YamlModel model;
			if (dictionary.Count == 0)
			{
				log.LogWarning((object)(modName + ": every " + set.FilePattern + " file is gone, keeping the loaded configuration"));
			}
			else if (TryBuild(set, dictionary, out model, "reload"))
			{
				Publish(set, dictionary, model, writeBack: false);
				log.LogInfo((object)(modName + ": " + set.MainFileName + " reloaded" + (IsAuthor ? "" : ", not applied, remote configuration active")));
			}
		}

		private void Publish(YamlFileSet set, IReadOnlyDictionary<string, string> files, YamlModel model, bool writeBack)
		{
			bool suppressWriteBack = SuppressWriteBack;
			(YamlFileSet, YamlModel)? tuple = built;
			SuppressWriteBack = suppressWriteBack || !writeBack;
			built = (set, model);
			try
			{
				if (set.Channel == null)
				{
					Receive(set, files);
				}
				else
				{
					set.Channel.Assign(YamlFileList.Flatten(files));
				}
			}
			finally
			{
				SuppressWriteBack = suppressWriteBack;
				built = tuple;
			}
		}

		private void Receive(YamlFileSet set, IReadOnlyDictionary<string, string> files)
		{
			(YamlFileSet, YamlModel)? tuple = built;
			YamlModel? yamlModel;
			if (tuple.HasValue)
			{
				(YamlFileSet, YamlModel) valueOrDefault = tuple.GetValueOrDefault();
				if (valueOrDefault.Item1 == set)
				{
					yamlModel = valueOrDefault.Item2;
					goto IL_0031;
				}
			}
			yamlModel = BuildReceived(set, files);
			goto IL_0031;
			IL_0031:
			YamlModel yamlModel2 = yamlModel;
			if (yamlModel2 != null)
			{
				set.Files = files;
				set.Current = yamlModel2;
				if (CanApply())
				{
					Apply(set);
				}
				if (IsAuthor && !SuppressWriteBack)
				{
					store.WriteAll(files);
					watcher.TakeSnapshot(set);
				}
			}
		}

		private YamlModel? BuildReceived(YamlFileSet set, IReadOnlyDictionary<string, string> files)
		{
			if (TryBuild(set, files, out YamlModel model, "received"))
			{
				return model;
			}
			foreach (KeyValuePair<string, string> file in files)
			{
				log.LogWarning((object)(modName + ": content of " + file.Key + ":\n" + file.Value));
			}
			return null;
		}

		private void Apply(YamlFileSet set)
		{
			if (set.Current == null || !set.Enabled())
			{
				return;
			}
			try
			{
				set.Apply(set.Current);
				this.Applied?.Invoke(set);
			}
			catch (Exception arg)
			{
				log.LogError((object)$"{modName}: applying {set.MainFileName} failed: {arg}");
			}
		}
	}
	internal static class YamlFileList
	{
		public static List<string> Flatten(IReadOnlyDictionary<string, string> files)
		{
			List<string> list = new List<string>(files.Count * 2);
			foreach (KeyValuePair<string, string> file in files)
			{
				list.Add(file.Key);
				list.Add(file.Value);
			}
			return list;
		}

		public static Dictionary<string, string> Unflatten(List<string>? list)
		{
			Dictionary<string, string> dictionary = new Dictionary<string, string>();
			int num = 0;
			while (list != null && num + 1 < list.Count)
			{
				dictionary[list[num]] = list[num + 1];
				num += 2;
			}
			return dictionary;
		}
	}
	public sealed class YamlFileSet
	{
		internal int ReloadPending;

		private Regex? nameFilter;

		public string FilePattern { get; }

		public string MainFileName { get; }

		public string SyncKey { get; }

		public Func<YamlModel> CreateModel { get; }

		public Action<YamlModel> Apply { get; }

		public Func<bool> Enabled { get; set; } = () => true;

		public Func<byte[]?>? DefaultContent { get; set; }

		public Func<string> EditorLabel { get; set; }

		public IReadOnlyDictionary<string, string> Files { get; internal set; } = new Dictionary<string, string>();

		public YamlModel? Current { get; internal set; }

		internal Article<List<string>>? Channel { get; set; }

		internal Dictionary<string, DateTime> Snapshot { get; } = new Dictionary<string, DateTime>(StringComparer.OrdinalIgnoreCase);

		internal Regex NameFilter => nameFilter ?? (nameFilter = new Regex("^" + Regex.Escape(FilePattern).Replace("\\*", ".*") + "$", RegexOptions.IgnoreCase));

		public YamlFileSet(string filePattern, string syncKey, Func<YamlModel> createModel, Action<YamlModel> apply)
		{
			FilePattern = filePattern;
			MainFileName = filePattern.Replace("*", "");
			SyncKey = syncKey;
			CreateModel = createModel;
			Apply = apply;
			EditorLabel = () => "Edit " + MainFileName;
		}
	}
	internal sealed class YamlFileStore
	{
		private readonly string modName;

		private readonly ManualLogSource log;

		private readonly List<string> searchFolders;

		public IReadOnlyList<string> SearchFolders => searchFolders;

		public YamlFileStore(string modName, ManualLogSource log, IEnumerable<string> searchFolders)
		{
			this.modName = modName;
			this.log = log;
			this.searchFolders = searchFolders.ToList();
		}

		public List<string> Discover(YamlFileSet set)
		{
			Regex nameFilter = set.NameFilter;
			List<string> list = new List<string>();
			foreach (string item in searchFolders.Where(Directory.Exists))
			{
				list.AddRange(from p in Directory.GetFiles(item, set.FilePattern)
					where nameFilter.IsMatch(Path.GetFileName(p))
					select p);
			}
			return list.OrderBy((string p) => (!string.Equals(Path.GetFileName(p), set.MainFileName, StringComparison.OrdinalIgnoreCase)) ? 1 : 0).ThenBy<string, string>(Path.GetFileName, StringComparer.OrdinalIgnoreCase).ToList();
		}

		public Dictionary<string, string> Read(IEnumerable<string> paths)
		{
			Dictionary<string, string> dictionary = new Dictionary<string, string>();
			foreach (string path in paths)
			{
				try
				{
					dictionary[path] = File.ReadAllText(path);
				}
				catch (Exception ex)
				{
					log.LogError((object)(modName + ": reading " + path + " failed: " + ex.Message));
				}
			}
			return dictionary;
		}

		public Dictionary<string, string> ReadOrCreate(YamlFileSet set)
		{
			Dictionary<string, string> dictionary = Read(Discover(set));
			if (dictionary.Count <= 0)
			{
				return WriteDefault(set);
			}
			return dictionary;
		}

		public Dictionary<string, DateTime> WriteTimes(YamlFileSet set)
		{
			Dictionary<string, DateTime> dictionary = new Dictionary<string, DateTime>(StringComparer.OrdinalIgnoreCase);
			foreach (string item in Discover(set))
			{
				dictionary[item] = File.GetLastWriteTimeUtc(item);
			}
			return dictionary;
		}

		public void WriteAll(IReadOnlyDictionary<string, string> files)
		{
			foreach (KeyValuePair<string, string> file in files)
			{
				WriteAtomic(file.Key, file.Value);
			}
		}

		private Dictionary<string, string> WriteDefault(YamlFileSet set)
		{
			byte[] array = set.DefaultContent?.Invoke();
			if (array == null || searchFolders.Count == 0)
			{
				return new Dictionary<string, string>();
			}
			string text = Path.Combine(searchFolders[0], set.MainFileName);
			try
			{
				Directory.CreateDirectory(searchFolders[0]);
				File.WriteAllBytes(text, array);
				log.LogInfo((object)(modName + ": wrote default " + text));
			}
			catch (Exception ex)
			{
				log.LogError((object)(modName + ": writing default " + text + " failed: " + ex.Message));
				return new Dictionary<string, string>();
			}
			return Read(new string[1] { text });
		}

		private void WriteAtomic(string path, string content)
		{
			try
			{
				if (!File.Exists(path) || !(File.ReadAllText(path) == content))
				{
					ReplaceThroughTempFile(path, content);
					log.LogInfo((object)(modName + ": wrote " + path));
				}
			}
			catch (Exception ex)
			{
				log.LogError((object)(modName + ": writing " + path + " failed: " + ex.Message));
			}
		}

		private static void ReplaceThroughTempFile(string path, string content)
		{
			string text = path + ".tmp";
			File.WriteAllText(text, content, new UTF8Encoding(encoderShouldEmitUTF8Identifier: false));
			if (File.Exists(path))
			{
				File.Replace(text, path, null);
			}
			else
			{
				File.Move(text, path);
			}
		}
	}
	internal sealed class YamlFileWatcher
	{
		private const int PollMilliseconds = 5000;

		private readonly YamlFileStore store;

		private readonly IReadOnlyList<YamlFileSet> sets;

		private readonly Action<YamlFileSet> reload;

		private readonly ManualLogSource log;

		private readonly string modName;

		private Timer? timer;

		public YamlFileWatcher(YamlFileStore store, IReadOnlyList<YamlFileSet> sets, Action<YamlFileSet> reload, ManualLogSource log, string modName)
		{
			this.store = store;
			this.sets = sets;
			this.reload = reload;
			this.log = log;
			this.modName = modName;
		}

		public void Start()
		{
			if (timer == null)
			{
				timer = new Timer(delegate
				{
					Poll();
				}, null, 5000, 5000);
			}
		}

		public Dictionary<string, string> ReadOrCreate(YamlFileSet set)
		{
			TakeSnapshot(set);
			bool flag;
			lock (set.Snapshot)
			{
				flag = set.Snapshot.Count > 0;
			}
			Dictionary<string, string> dictionary = store.ReadOrCreate(set);
			if (!flag && dictionary.Count > 0)
			{
				TakeSnapshot(set);
			}
			return dictionary;
		}

		public Dictionary<string, string> Read(YamlFileSet set)
		{
			TakeSnapshot(set);
			return store.Read(store.Discover(set));
		}

		public void TakeSnapshot(YamlFileSet set)
		{
			Dictionary<string, DateTime> dictionary = store.WriteTimes(set);
			lock (set.Snapshot)
			{
				set.Snapshot.Clear();
				foreach (KeyValuePair<string, DateTime> item in dictionary)
				{
					set.Snapshot[item.Key] = item.Value;
				}
			}
		}

		private void Poll()
		{
			try
			{
				foreach (YamlFileSet set in sets.Where(HasChanged))
				{
					if (Interlocked.CompareExchange(ref set.ReloadPending, 1, 0) == 0)
					{
						ThreadingHelper.SynchronizingObject.BeginInvoke((Action)delegate
						{
							Reload(set);
						}, null);
					}
				}
			}
			catch (Exception ex)
			{
				log.LogError((object)(modName + ": watching YAML files failed: " + ex.Message));
			}
		}

		private bool HasChanged(YamlFileSet set)
		{
			Dictionary<string, DateTime> dictionary = store.WriteTimes(set);
			lock (set.Snapshot)
			{
				DateTime value;
				return dictionary.Count != set.Snapshot.Count || dictionary.Any((KeyValuePair<string, DateTime> pair) => !set.Snapshot.TryGetValue(pair.Key, out value) || value != pair.Value);
			}
		}

		private void Reload(YamlFileSet set)
		{
			try
			{
				reload(set);
			}
			catch (Exception arg)
			{
				log.LogError((object)$"{modName}: reloading {set.MainFileName} failed: {arg}");
			}
			finally
			{
				Interlocked.Exchange(ref set.ReloadPending, 0);
			}
		}
	}
	internal static class YamlGameHooks
	{
		private static class Patches
		{
			public static void AfterMenuStart()
			{
				LoadHooked();
			}

			public static void AfterNetworkStart(ZNet __instance)
			{
				if (__instance.IsServer() && __instance.IsDedicated())
				{
					LoadHooked();
				}
			}

			public static void AfterRespawnRequest(Game __instance)
			{
				object obj = firstSpawnField?.GetValue(__instance);
				if (obj is bool && (bool)obj)
				{
					ApplyHooked();
				}
			}
		}

		private static readonly List<YamlFileHub> hooked = new List<YamlFileHub>();

		private static readonly FieldInfo? firstSpawnField = AccessTools.Field(typeof(Game), "m_firstSpawn");

		private static bool patched;

		public static void Install(YamlFileHub hub, Harmony harmony, int applyPriority)
		{
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Expected O, but got Unknown
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: Expected O, but got Unknown
			//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00af: Expected O, but got Unknown
			if (!hooked.Contains(hub))
			{
				hooked.Add(hub);
			}
			if (!patched)
			{
				patched = true;
				harmony.Patch((MethodBase)AccessTools.Method(typeof(FejdStartup), "Start", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(Patches), "AfterMenuStart", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				harmony.Patch((MethodBase)AccessTools.Method(typeof(ZNet), "Awake", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(Patches), "AfterNetworkStart", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				HarmonyMethod val = new HarmonyMethod(typeof(Patches), "AfterRespawnRequest", (Type[])null)
				{
					priority = applyPriority
				};
				harmony.Patch((MethodBase)AccessTools.Method(typeof(Game), "RequestRespawn", (Type[])null, (Type[])null), (HarmonyMethod)null, val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			}
		}

		private static void LoadHooked()
		{
			foreach (YamlFileHub item in hooked)
			{
				item.LoadFromHook();
			}
		}

		private static void ApplyHooked()
		{
			foreach (YamlFileHub item in hooked)
			{
				item.ApplyFromHook();
			}
		}
	}
	internal sealed class YamlGroupTable
	{
		private readonly Dictionary<string, List<string>> members = new Dictionary<string, List<string>>(StringComparer.OrdinalIgnoreCase);

		private readonly Dictionary<string, List<string>> parents = new Dictionary<string, List<string>>(StringComparer.OrdinalIgnoreCase);

		public IReadOnlyCollection<string> Names => members.Keys;

		public bool IsGroup(string name)
		{
			return members.ContainsKey(name);
		}

		public void Add(string group, IEnumerable<string> names)
		{
			if (!members.TryGetValue(group, out List<string> value))
			{
				value = new List<string>();
				members.Add(group, value);
			}
			foreach (string name in names)
			{
				value.Add(name);
				if (!parents.TryGetValue(name, out List<string> value2))
				{
					value2 = new List<string>();
					parents.Add(name, value2);
				}
				value2.Add(group);
			}
		}

		public IReadOnlyList<string> Resolve(string name)
		{
			List<string> list = new List<string>();
			HashSet<string> hashSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
			Queue<string> queue = new Queue<string>();
			queue.Enqueue(name);
			while (queue.Count > 0)
			{
				if (!parents.TryGetValue(queue.Dequeue(), out List<string> value))
				{
					continue;
				}
				foreach (string item in value)
				{
					if (hashSet.Add(item))
					{
						list.Add(item);
						queue.Enqueue(item);
					}
				}
			}
			return list;
		}

		public IReadOnlyList<string> Expand(string group)
		{
			List<string> result = new List<string>();
			HashSet<string> seen = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
			Expand(group, seen, result);
			return result;
		}

		private void Expand(string name, HashSet<string> seen, List<string> result)
		{
			if (!seen.Add(name))
			{
				return;
			}
			if (!members.TryGetValue(name, out List<string> value))
			{
				result.Add(name);
				return;
			}
			foreach (string item in value)
			{
				Expand(item, seen, result);
			}
		}
	}
	public abstract class YamlModel
	{
		private static readonly IDeserializer Parser = new DeserializerBuilder().Build();

		private string? currentFile;

		public List<string> Errors { get; } = new List<string>();

		public List<string> Warnings { get; } = new List<string>();

		protected YamlGroupTable Groups { get; } = new YamlGroupTable();

		public void Load(string yamlText)
		{
			ReadText(yamlText);
			Verify();
		}

		public void LoadAll(IEnumerable<KeyValuePair<string, string>> files)
		{
			foreach (KeyValuePair<string, string> file in files)
			{
				currentFile = Path.GetFileName(file.Key);
				ReadText(file.Value);
			}
			currentFile = null;
			Verify();
		}

		public IReadOnlyList<string> ResolveGroups(string name)
		{
			return Groups.Resolve(name);
		}

		protected abstract void Read(YamlNode root);

		protected virtual void Verify()
		{
		}

		protected void ReadGroups(YamlNode root, string key = "groups")
		{
			foreach (KeyValuePair<string, YamlNode> entry in root.Get(key).Entries)
			{
				if (entry.Value.TryStringList(out List<string> items))
				{
					Groups.Add(entry.Key, items);
				}
			}
		}

		internal void AddError(string message)
		{
			Errors.Add(WithFile(message));
		}

		internal void AddWarning(string message)
		{
			Warnings.Add(WithFile(message));
		}

		private string WithFile(string message)
		{
			if (currentFile != null)
			{
				return currentFile + ": " + message;
			}
			return message;
		}

		private void ReadText(string yamlText)
		{
			if (TryParse(yamlText, out object graph) && graph != null)
			{
				if (!(graph is IDictionary<object, object>))
				{
					AddError("the file must be a map of keys at the top level, not " + ((graph is IList<object>) ? "a list" : "a single value"));
					return;
				}
				YamlNode yamlNode = new YamlNode(this, graph, "");
				Read(yamlNode);
				yamlNode.WarnUnknownKeys();
			}
		}

		private bool TryParse(string yamlText, out object? graph)
		{
			try
			{
				graph = Parser.Deserialize<object>(yamlText.TrimStart(new char[1] { '\ufeff' }));
				return true;
			}
			catch (Exception ex)
			{
				AddError("invalid YAML: " + ex.Message + ((ex.InnerException == null) ? "" : (" (" + ex.InnerException.Message + ")")));
				graph = null;
				return false;
			}
		}
	}
	internal enum YamlNodeKind
	{
		Scalar,
		List,
		Map,
		Null,
		Missing
	}
	internal sealed class YamlNode
	{
		private readonly YamlModel model;

		private readonly object? value;

		private List<KeyValuePair<string, YamlNode>>? mapEntries;

		private Dictionary<string, YamlNode>? mapLookup;

		private List<YamlNode>? listItems;

		private HashSet<string>? askedKeys;

		private bool visited;

		private bool shapeReported;

		public string Path { get; }

		public YamlNodeKind Kind { get; }

		public string? Text
		{
			get
			{
				if (Kind != YamlNodeKind.Scalar)
				{
					return null;
				}
				return Convert.ToString(value, CultureInfo.InvariantCulture);
			}
		}

		public int Count => Kind switch
		{
			YamlNodeKind.Map => MapEntries.Count, 
			YamlNodeKind.List => ListItems.Count, 
			_ => 0, 
		};

		public IReadOnlyList<KeyValuePair<string, YamlNode>> Entries
		{
			get
			{
				if (!ExpectShape(YamlNodeKind.Map, "a map"))
				{
					return Array.Empty<KeyValuePair<string, YamlNode>>();
				}
				visited = true;
				AskedKeys.UnionWith(MapEntries.Select<KeyValuePair<string, YamlNode>, string>((KeyValuePair<string, YamlNode> e) => e.Key));
				return MapEntries;
			}
		}

		public IReadOnlyList<YamlNode> Items
		{
			get
			{
				if (!ExpectShape(YamlNodeKind.List, "a list"))
				{
					return Array.Empty<YamlNode>();
				}
				return ListItems;
			}
		}

		private HashSet<string> AskedKeys => askedKeys ?? (askedKeys = new HashSet<string>(StringComparer.OrdinalIgnoreCase));

		private List<KeyValuePair<string, YamlNode>> MapEntries
		{
			get
			{
				if (mapEntries == null)
				{
					BuildMap();
				}
				return mapEntries;
			}
		}

		private Dictionary<string, YamlNode> MapLookup
		{
			get
			{
				if (mapLookup == null)
				{
					BuildMap();
				}
				return mapLookup;
			}
		}

		private List<YamlNode> ListItems
		{
			get
			{
				if (listItems == null)
				{
					IList<object> source = (IList<object>)value;
					listItems = source.Select((object item, int i) => new YamlNode(model, item, $"{Path}[{i}]")).ToList();
				}
				return listItems;
			}
		}

		internal YamlNode(YamlModel model, object? value, string path, YamlNodeKind kind)
		{
			this.model = model;
			this.value = value;
			Path = path;
			Kind = kind;
		}

		internal YamlNode(YamlModel model, object? value, string path)
			: this(model, value, path, KindOf(value))
		{
		}

		public YamlNode Get(string key)
		{
			string path = (string.IsNullOrEmpty(Path) ? key : (Path + "." + key));
			if (!ExpectShape(YamlNodeKind.Map, "a map"))
			{
				return new YamlNode(model, null, path, YamlNodeKind.Missing);
			}
			visited = true;
			AskedKeys.Add(key);
			if (!MapLookup.TryGetValue(key, out YamlNode result))
			{
				return new YamlNode(model, null, path, YamlNodeKind.Missing);
			}
			return result;
		}

		public bool Is(string keyword)
		{
			if (Kind == YamlNodeKind.Scalar)
			{
				return string.Equals(Text.Trim(), keyword, StringComparison.OrdinalIgnoreCase);
			}
			return false;
		}

		public void Error(string message)
		{
			model.AddError(Prefixed(message));
		}

		public void Warn(string message)
		{
			model.AddWarning(Prefixed(message));
		}

		public void WarnUnknownKeys()
		{
			if (Kind == YamlNodeKind.Map && visited)
			{
				List<string> list = (from e in MapEntries
					select e.Key into k
					where !AskedKeys.Contains(k)
					select k).ToList();
				if (list.Count > 0)
				{
					Warn("unknown keys " + string.Join(", ", list));
				}
				{
					foreach (KeyValuePair<string, YamlNode> mapEntry in MapEntries)
					{
						mapEntry.Value.WarnUnknownKeys();
					}
					return;
				}
			}
			if (Kind == YamlNodeKind.List && listItems != null)
			{
				listItems.ForEach(delegate(YamlNode item)
				{
					item.WarnUnknownKeys();
				});
			}
		}

		private string Prefixed(string message)
		{
			if (!string.IsNullOrEmpty(Path))
			{
				return Path + ": " + message;
			}
			return message;
		}

		private void BuildMap()
		{
			mapEntries = new List<KeyValuePair<string, YamlNode>>();
			mapLookup = new Dictionary<string, YamlNode>(StringComparer.OrdinalIgnoreCase);
			foreach (KeyValuePair<object, object> item in (IDictionary<object, object>)value)
			{
				string text = Convert.ToString(item.Key, CultureInfo.InvariantCulture) ?? "";
				string path = (string.IsNullOrEmpty(Path) ? text : (Path + "." + text));
				YamlNode yamlNode = new YamlNode(model, item.Value, path);
				if (mapLookup.ContainsKey(text))
				{
					yamlNode.Warn("duplicate key (differs only in case), ignored");
					continue;
				}
				mapLookup.Add(text, yamlNode);
				mapEntries.Add(new KeyValuePair<string, YamlNode>(text, yamlNode));
			}
		}

		private bool ExpectShape(YamlNodeKind wanted, string description)
		{
			if (Kind == wanted)
			{
				return true;
			}
			if (Kind != YamlNodeKind.Missing && !shapeReported)
			{
				shapeReported = true;
				Error((Kind == YamlNodeKind.Null) ? ("expected " + description + " but the key has no value") : ("expected " + description + ", found " + Describe(Kind)));
			}
			return false;
		}

		private static string Describe(YamlNodeKind kind)
		{
			return kind switch
			{
				YamlNodeKind.Scalar => "a single value", 
				YamlNodeKind.List => "a list", 
				YamlNodeKind.Map => "a map", 
				_ => "nothing", 
			};
		}

		private static YamlNodeKind KindOf(object? value)
		{
			if (value != null)
			{
				if (!(value is IDictionary<object, object>))
				{
					if (value is IList<object>)
					{
						return YamlNodeKind.List;
					}
					return YamlNodeKind.Scalar;
				}
				return YamlNodeKind.Map;
			}
			return YamlNodeKind.Null;
		}

		public bool TryInt(out int result)
		{
			return TryScalar<int>(out result);
		}

		public bool TryFloat(out float result)
		{
			return TryScalar<float>(out result);
		}

		public bool TryBool(out bool result)
		{
			return TryScalar<bool>(out result);
		}

		public bool TryString(out string result)
		{
			return TryScalar<string>(out result);
		}

		public bool TryEnum<T>(out T result) where T : struct, Enum
		{
			return TryScalar<T>(out result);
		}

		public bool TryList<T>(out List<T> items)
		{
			items = new List<T>();
			if (!ExpectShape(YamlNodeKind.List, "a list of " + YamlScalarParser.Plural<T>()))
			{
				return false;
			}
			bool result = true;
			foreach (YamlNode listItem in ListItems)
			{
				if (listItem.TryScalar<T>(out var result2))
				{
					items.Add(result2);
				}
				else
				{
					result = false;
				}
			}
			return result;
		}

		public bool TryStringList(out List<string> items)
		{
			if (Kind == YamlNodeKind.Scalar)
			{
				items = new List<string> { Text };
				return true;
			}
			return TryList(out items);
		}

		private bool TryScalar<T>(out T result)
		{
			result = default(T);
			if (!ExpectShape(YamlNodeKind.Scalar, YamlScalarParser.Describe(typeof(T))))
			{
				return false;
			}
			if (YamlScalarParser.TryParse(Text, typeof(T), out object result2))
			{
				result = (T)result2;
				return true;
			}
			Error("'" + Text + "' is not " + YamlScalarParser.Describe(typeof(T)));
			return false;
		}
	}
	internal static class YamlScalarParser
	{
		private static readonly string[] TrueWords = new string[4] { "true", "yes", "on", "1" };

		private static readonly string[] FalseWords = new string[4] { "false", "no", "off", "0" };

		public static bool TryParse(string text, Type type, out object? result)
		{
			string text2 = text.Trim();
			result = null;
			if (type == typeof(string))
			{
				result = text;
				return true;
			}
			if (type == typeof(int))
			{
				int result3;
				bool result2 = int.TryParse(text2, NumberStyles.Integer, CultureInfo.InvariantCulture, out result3);
				result = result3;
				return result2;
			}
			if (type == typeof(float))
			{
				float result5;
				bool result4 = float.TryParse(text2, NumberStyles.Float, CultureInfo.InvariantCulture, out result5);
				result = result5;
				return result4;
			}
			if (!(type == typeof(bool)))
			{
				return TryParseEnum(text2, type, out result);
			}
			return TryParseBool(text2, out result);
		}

		public static string Describe(Type type)
		{
			if (type == typeof(int))
			{
				return "an integer";
			}
			if (type == typeof(float))
			{
				return "a number";
			}
			if (type == typeof(bool))
			{
				return "true or false";
			}
			if (!type.IsEnum)
			{
				return "text";
			}
			return "one of " + string.Join(", ", Enum.GetNames(type));
		}

		public static string Plural<T>()
		{
			if (!(typeof(T) == typeof(int)))
			{
				if (!(typeof(T) == typeof(float)))
				{
					if (!(typeof(T) == typeof(bool)))
					{
						if (!typeof(T).IsEnum)
						{
							return "text values";
						}
						return "names (" + string.Join(", ", Enum.GetNames(typeof(T))) + ")";
					}
					return "true/false values";
				}
				return "numbers";
			}
			return "integers";
		}

		private static bool TryParseBool(string text, out object? result)
		{
			if (TrueWords.Contains<string>(text, StringComparer.OrdinalIgnoreCase))
			{
				result = true;
				return true;
			}
			result = false;
			return FalseWords.Contains<string>(text, StringComparer.OrdinalIgnoreCase);
		}

		private static bool TryParseEnum(string text, Type type, out object? result)
		{
			if (!type.IsEnum)
			{
				throw new NotSupportedException("YamlNode cannot read values of type " + type.Name);
			}
			string text2 = Enum.GetNames(type).FirstOrDefault((string n) => string.Equals(n, text, StringComparison.OrdinalIgnoreCase));
			result = ((text2 == null) ? null : Enum.Parse(type, text2));
			return text2 != null;
		}
	}
}
namespace ConfigReload
{
	internal static class ConfigReloader
	{
		private sealed class Poller : IDisposable
		{
			private const int IntervalMilliseconds = 5000;

			private readonly ConfigFile config;

			private readonly ManualLogSource? log;

			private readonly string fileName;

			private readonly Timer timer;

			private DateTime lastWriteTime;

			private long lastLength;

			private string lastContent;

			private int reloading;

			public Poller(ConfigFile config, ManualLogSource? log)
			{
				this.config = config;
				this.log = log;
				fileName = Path.GetFileName(config.ConfigFilePath);
				Snapshot(out lastWriteTime, out lastLength);
				lastContent = ReadOrNull(config.ConfigFilePath) ?? "";
				timer = new Timer(delegate
				{
					Poll();
				}, null, 5000, 5000);
			}

			private void Poll()
			{
				try
				{
					if (SnapshotChanged())
					{
						string text = ReadOrNull(config.ConfigFilePath);
						if (text != null && !(text == lastContent))
						{
							lastContent = text;
							ScheduleCheck();
						}
					}
				}
				catch (Exception ex)
				{
					ManualLogSource? obj = log;
					if (obj != null)
					{
						obj.LogError((object)("Watching " + fileName + " failed: " + ex.Message));
					}
				}
			}

			private bool SnapshotChanged()
			{
				Snapshot(out var writeTime, out var length);
				if (writeTime == lastWriteTime && length == lastLength)
				{
					return false;
				}
				lastWriteTime = writeTime;
				lastLength = length;
				return true;
			}

			private void ScheduleCheck()
			{
				if (Interlocked.CompareExchange(ref reloading, 1, 0) == 0)
				{
					ThreadingHelper.SynchronizingObject.BeginInvoke(new Action(CheckAndReload), null);
				}
			}

			private void CheckAndReload()
			{
				try
				{
					if (!LoadedValues.Matches(config, lastContent))
					{
						ManualLogSource? obj = log;
						if (obj != null)
						{
							obj.LogInfo((object)(fileName + " changed on disk, reloading"));
						}
						ReloadQuietly();
					}
				}
				catch (Exception ex)
				{
					ManualLogSource? obj2 = log;
					if (obj2 != null)
					{
						obj2.LogError((object)("Failed to reload " + fileName + ", please check it for typos and formatting: " + ex.Message));
					}
				}
				finally
				{
					Resnapshot();
					Interlocked.Exchange(ref reloading, 0);
				}
			}

			private void ReloadQuietly()
			{
				bool saveOnConfigSet = config.SaveOnConfigSet;
				config.SaveOnConfigSet = false;
				try
				{
					config.Reload();
				}
				finally
				{
					config.SaveOnConfigSet = saveOnConfigSet;
				}
				if (saveOnConfigSet)
				{
					config.Save();
				}
			}

			private void Resnapshot()
			{
				Snapshot(out lastWriteTime, out lastLength);
				lastContent = ReadOrNull(config.ConfigFilePath) ?? lastContent;
			}

			private void Snapshot(out DateTime writeTime, out long length)
			{
				FileInfo fileInfo = new FileInfo(config.ConfigFilePath);
				if (fileInfo.Exists)
				{
					writeTime = fileInfo.LastWriteTimeUtc;
					length = fileInfo.Length;
				}
				else
				{
					writeTime = DateTime.MinValue;
					length = -1L;
				}
			}

			private static string? ReadOrNull(string path)
			{
				try
				{
					return File.Exists(path) ? File.ReadAllText(path) : null;
				}
				catch (IOException)
				{
					return null;
				}
			}

			public void Dispose()
			{
				timer.Dispose();
			}
		}

		private static readonly HashSet<ConfigFile> held = new HashSet<ConfigFile>();

		public static void HoldSaves(ConfigFile config)
		{
			if (config.SaveOnConfigSet && held.Add(config))
			{
				config.SaveOnConfigSet = false;
			}
		}

		public static IDisposable Setup(ConfigFile config, ManualLogSource? log = null, bool saveNow = true)
		{
			if (held.Remove(config))
			{
				config.SaveOnConfigSet = true;
			}
			if (saveNow)
			{
				config.Save();
			}
			return new Poller(config, log);
		}
	}
	internal static class LoadedValues
	{
		private static readonly PropertyInfo? orphans = typeof(ConfigFile).GetProperty("OrphanedEntries", BindingFlags.Instance | BindingFlags.NonPublic);

		public static bool Matches(ConfigFile config, string content)
		{
			Dictionary<ConfigDefinition, string> dictionary = Parse(content);
			Dictionary<ConfigDefinition, string> dictionary2 = Loaded(config);
			if (dictionary2 == null)
			{
				return false;
			}
			if (dictionary.Count != dictionary2.Count)
			{
				return false;
			}
			foreach (KeyValuePair<ConfigDefinition, string> item in dictionary)
			{
				if (!dictionary2.TryGetValue(item.Key, out var value) || !string.Equals(value, item.Value, StringComparison.Ordinal))
				{
					return false;
				}
			}
			return true;
		}

		private static Dictionary<ConfigDefinition, string>? Loaded(ConfigFile config)
		{
			if (!(orphans?.GetValue(config) is Dictionary<ConfigDefinition, string> dictionary))
			{
				return null;
			}
			Dictionary<ConfigDefinition, string> dictionary2 = new Dictionary<ConfigDefinition, string>(dictionary);
			foreach (KeyValuePair<ConfigDefinition, ConfigEntryBase> item in config)
			{
				dictionary2[item.Key] = item.Value.GetSerializedValue();
			}
			return dictionary2;
		}

		private static Dictionary<ConfigDefinition, string> Parse(string content)
		{
			//IL_009a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b4: Expected O, but got Unknown
			Dictionary<ConfigDefinition, string> dictionary = new Dictionary<ConfigDefinition, string>();
			string text = "";
			string[] array = content.Split(new char[1] { '\n' });
			for (int i = 0; i < array.Length; i++)
			{
				string text2 = array[i].Trim();
				if (text2.Length == 0 || text2.StartsWith("#", StringComparison.Ordinal))
				{
					continue;
				}
				if (text2.StartsWith("[", StringComparison.Ordinal) && text2.EndsWith("]", StringComparison.Ordinal))
				{
					text = text2.Substring(1, text2.Length - 2);
					continue;
				}
				int num = text2.IndexOf('=');
				if (num > 0)
				{
					dictionary[new ConfigDefinition(text, text2.Substring(0, num).Trim())] = text2.Substring(num + 1).Trim();
				}
			}
			return dictionary;
		}
	}
}
namespace Charter
{
	internal sealed class Amendments
	{
		private readonly Charter charter;

		private readonly Ledger ledger;

		private readonly Journal journal;

		private readonly string rpcName;

		public Amendments(Charter charter, Ledger ledger, Journal journal)
		{
			this.charter = charter;
			this.ledger = ledger;
			this.journal = journal;
			rpcName = "Charter_" + charter.Guid + "_Amend";
		}

		public void Send(IClause clause)
		{
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Expected O, but got Unknown
			ZRpc rpc = charter.Receiver.Rpc;
			if (rpc != null && rpc.IsConnected())
			{
				string currentToml = clause.CurrentToml;
				ZPackage val = new ZPackage();
				val.Write(1);
				val.Write(charter.Guid);
				val.Write(clause.Section);
				val.Write(clause.Key);
				val.Write(currentToml);
				rpc.Invoke(rpcName, new object[1] { val });
				journal.Info("amendment sent: " + clause.Section + "." + clause.Key + " = " + currentToml);
			}
		}

		public void OnAmend(ZRpc rpc, ZPackage pkg)
		{
			try
			{
				Receive(rpc, pkg);
			}
			catch (Exception arg)
			{
				journal.Error($"handling an amendment failed: {arg}");
			}
		}

		private void Receive(ZRpc rpc, ZPackage pkg)
		{
			if (pkg.ReadInt() != 1 || pkg.ReadString() != charter.Guid)
			{
				journal.Warning("amendment with a foreign protocol or guid dropped");
				return;
			}
			string section = pkg.ReadString();
			string key = pkg.ReadString();
			string toml = pkg.ReadString();
			ZNetPeer val = Side.PeerOf(rpc);
			if (val != null && Side.IsServer)
			{
				string text = Accept(val, section, key, toml);
				if (text != null)
				{
					Reject(val, section, key, text);
				}
			}
		}

		private string? Accept(ZNetPeer peer, string section, string key, string toml)
		{
			if (!Stewardship.IsSteward(peer))
			{
				return "you are not a steward";
			}
			IClause clause = ledger.Find(section, key);
			if (clause == null)
			{
				return "unknown clause";
			}
			if (clause.Local)
			{
				return "the clause is local";
			}
			try
			{
				clause.Entry.SetSerializedValue(toml);
			}
			catch (Exception ex)
			{
				return "bad value: " + ex.Message;
			}
			clause.Entry.ConfigFile.Save();
			journal.Info("amendment by " + Side.NameOf(peer) + " accepted: " + section + "." + key + " = " + toml);
			return null;
		}

		private void Reject(ZNetPeer peer, string section, string key, string reason)
		{
			journal.Warning("amendment by " + Side.NameOf(peer) + " rejected: " + section + "." + key + ": " + reason);
			charter.Publisher.SendNotice(peer, ledger.Find(section, key), "amendment of " + section + "." + key + " rejected: " + reason);
		}
	}
	internal sealed class Article<T> : IArticle
	{
		private readonly Charter charter;

		private readonly ArticleKind kind;

		private T value;

		public string Name { get; }

		public bool Standing { get; }

		public T Value => value;

		ArticleKind IArticle.Kind => kind;

		object IArticle.Boxed => value;

		public event Action? Changed;

		public Article(Charter charter, string name, T initial, bool standing = false)
		{
			kind = ArticleValues.KindOf(typeof(T));
			this.charter = charter;
			Name = name;
			Standing = standing;
			value = initial;
			charter.Ledger.Add(this);
		}

		public void Assign(T value)
		{
			if (!charter.IsAuthor)
			{
				charter.Journal.Warning("article '" + Name + "' cannot be assigned while the server's charter binds");
			}
			else if (!ArticleValues.Same(this.value, value))
			{
				this.value = value;
				Raise();
				charter.Publisher.MarkDirty(this);
			}
		}

		bool IArticle.Accept(object incoming)
		{
			if (ArticleValues.Same(value, incoming))
			{
				return false;
			}
			value = (T)incoming;
			return true;
		}

		void IArticle.Raise()
		{
			Raise();
		}

		private void Raise()
		{
			try
			{
				this.Changed?.Invoke();
			}
			catch (Exception arg)
			{
				charter.Journal.Error($"a Changed handler of article '{Name}' threw: {arg}");
			}
		}
	}
	internal interface IArticle
	{
		string Name { get; }

		bool Standing { get; }

		ArticleKind Kind { get; }

		object Boxed { get; }

		bool Accept(object incoming);

		void Raise();
	}
	internal enum ArticleKind : byte
	{
		Text = 1,
		TextList,
		Integer,
		Number,
		Toggle
	}
	internal static class ArticleValues
	{
		public static ArticleKind KindOf(Type type)
		{
			if (type == typeof(string))
			{
				return ArticleKind.Text;
			}
			if (type == typeof(List<string>))
			{
				return ArticleKind.TextList;
			}
			if (type == typeof(int))
			{
				return ArticleKind.Integer;
			}
			if (type == typeof(float))
			{
				return ArticleKind.Number;
			}
			if (type == typeof(bool))
			{
				return ArticleKind.Toggle;
			}
			throw new ArgumentException("Charter articles hold string, List<string>, int, float or bool, not " + type.Name);
		}

		public static void Write(ZPackage pkg, ArticleKind kind, object? value)
		{
			switch (kind)
			{
			case ArticleKind.Text:
				pkg.Write((value as string) ?? "");
				break;
			case ArticleKind.TextList:
				WriteList(pkg, value as List<string>);
				break;
			case ArticleKind.Integer:
				pkg.Write((value is int num2) ? num2 : 0);
				break;
			case ArticleKind.Number:
				pkg.Write((value is float num3) ? num3 : 0f);
				break;
			case ArticleKind.Toggle:
			{
				bool flag = default(bool);
				int num;
				if (value is bool)
				{
					flag = (bool)value;
					num = 1;
				}
				else
				{
					num = 0;
				}
				pkg.Write((byte)((uint)num & (flag ? 1u : 0u)) != 0);
				break;
			}
			default:
				throw new ArgumentException($"unknown article kind {kind}");
			}
		}

		public static object Read(ZPackage pkg, ArticleKind kind)
		{
			return kind switch
			{
				ArticleKind.Text => pkg.ReadString(), 
				ArticleKind.TextList => ReadList(pkg), 
				ArticleKind.Integer => pkg.ReadInt(), 
				ArticleKind.Number => pkg.ReadSingle(), 
				ArticleKind.Toggle => pkg.ReadBool(), 
				_ => throw new ArgumentException($"unknown article kind {kind}"), 
			};
		}

		public static int Size(ArticleKind kind, object? value)
		{
			return kind switch
			{
				ArticleKind.Text => (value as string)?.Length ?? 0, 
				ArticleKind.TextList => (value as List<string>)?.Sum((string s) => s?.Length ?? 0) ?? 0, 
				_ => 4, 
			};
		}

		public static bool Same(object? a, object? b)
		{
			if (a is List<string> first)
			{
				List<string> second = (b as List<string>) ?? new List<string>();
				return first.SequenceEqual<string>(second, StringComparer.Ordinal);
			}
			if (b is List<string> list)
			{
				if (list.Count == 0)
				{
					return a == null;
				}
				return false;
			}
			return object.Equals(a, b);
		}

		private static void WriteList(ZPackage pkg, List<string>? list)
		{
			pkg.Write(list?.Count ?? 0);
			foreach (string item in list ?? new List<string>())
			{
				pkg.Write(item ?? "");
			}
		}

		private static List<string> ReadList(ZPackage pkg)
		{
			int num = pkg.ReadInt();
			List<string> list = new List<string>(Math.Max(0, num));
			for (int i = 0; i < num; i++)
			{
				list.Add(pkg.ReadString());
			}
			return list;
		}
	}
	internal sealed class ChangeRouter
	{
		private readonly Charter charter;

		private readonly Ledger ledger;

		private readonly Journal journal;

		private readonly HashSet<ConfigFile> watched = new HashSet<ConfigFile>();

		public ChangeRouter(Charter charter, Ledger ledger, Journal journal)
		{
			this.charter = charter;
			this.ledger = ledger;
			this.journal = journal;
		}

		public void Watch(ConfigFile file)
		{
			if (watched.Add(file))
			{
				file.SettingChanged += OnSettingChanged;
			}
		}

		private void OnSettingChanged(object sender, SettingChangedEventArgs args)
		{
			try
			{
				if (!ledger.Applying)
				{
					Route(args.ChangedSetting);
				}
			}
			catch (Exception arg)
			{
				journal.Error($"handling a change of {args.ChangedSetting.Definition} failed: {arg}");
			}
		}

		private void Route(ConfigEntryBase entry)
		{
			IClause clause = ledger.Find(entry);
			if (clause == null)
			{
				return;
			}
			clause.RememberOwn();
			if (Side.IsServer)
			{
				charter.Publisher.MarkDirty(clause);
				return;
			}
			Receiver receiver = charter.Receiver;
			if (receiver.Bound && !clause.Local)
			{
				if (receiver.Steward)
				{
					charter.Amendments.Send(clause);
					return;
				}
				journal.Info(clause.Section + "." + clause.Key + " is bound by the server, reverting to " + clause.AuthorToml);
				clause.ApplyAuthor();
			}
		}
	}
	public sealed class Charter
	{
		private bool lastBound;

		private bool lastSteward;

		public string Guid { get; }

		public string Title { get; }

		public string Version { get; }

		public string OldestAccepted { get; }

		public bool Mandatory { get; }

		public bool IsBound
		{
			get
			{
				if (!Side.IsServer)
				{
					return Receiver.Bound;
				}
				return Ledger.BindingOn;
			}
		}

		public bool IsAuthor
		{
			get
			{
				if (!Side.IsServer)
				{
					return !Receiver.Bound;
				}
				return true;
			}
		}

		public bool MayAmend
		{
			get
			{
				if (IsBound)
				{
					return IsSteward;
				}
				return true;
			}
		}

		public bool IsSteward
		{
			get
			{
				if (!Side.IsServer)
				{
					return Receiver.Steward;
				}
				return true;
			}
		}

		public DateTime? LastPush
		{
			get
			{
				if (!Side.IsServer)
				{
					return Receiver.LastPush;
				}
				return null;
			}
		}

		public int PushedBytes => Receiver.PushedBytes;

		public static Verbosity Verbosity { get; set; } = Verbosity.Normal;

		internal Journal Journal { get; }

		internal Ledger Ledger { get; }

		internal Receiver Receiver { get; }

		internal Courier Courier { get; }

		internal Publisher Publisher { get; }

		internal Stewardship Stewardship { get; }

		internal Amendments Amendments { get; }

		internal ChangeRouter Router { get; }

		internal ReadOnlyTags Tags { get; }

		public event Action<bool>? Pushed;

		public event Action? StewardChanged;

		public event Action? BindingChanged;

		public Charter(string guid, string title, string version, string? oldestAccepted = null, bool mandatory = true)
		{
			Guid = guid;
			Title = title;
			Version = version;
			OldestAccepted = oldestAccepted ?? version;
			Mandatory = mandatory;
			Journal = new Journal(title);
			Ledger = new Ledger();
			Receiver = new Receiver(this, Ledger, Journal);
			Courier = new Courier(guid, Journal);
			Publisher = new Publisher(Ledger, Courier, Journal);
			Stewardship = new Stewardship(Courier, Publisher);
			Amendments = new Amendments(this, Ledger, Journal);
			Router = new ChangeRouter(this, Ledger, Journal);
			Tags = new ReadOnlyTags();
			lastSteward = IsSteward;
			Enrol();
		}

		public Clause<T> Clause<T>(ConfigEntry<T> entry, bool local = false)
		{
			Clause<T> clause = new Clause<T>(entry, local, Ledger);
			Ledger.Add(clause);
			Router.Watch(((ConfigEntryBase)entry).ConfigFile);
			return clause;
		}

		public void Binding(ConfigEntry<bool> entry)
		{
			Clause<bool> clause = Clause<bool>(entry);
			Ledger.SetBinding(clause, () => entry.Value);
		}

		public static void Install(Harmony harmony)
		{
			GameHooks.Install(harmony);
		}

		internal void Attach(ZNetPeer peer, bool server)
		{
			if (server)
			{
				peer.m_rpc.Register<ZPackage>("Charter_" + Guid + "_Amend", (Action<ZRpc, ZPackage>)Amendments.OnAmend);
			}
			else
			{
				peer.m_rpc.Register<ZPackage>("Charter_" + Guid + "_Push", (Action<ZRpc, ZPackage>)Receiver.OnPush);
			}
		}

		internal void Tick(float now)
		{
			Publisher.Flush();
			Courier.Tick();
			Stewardship.Tick(now);
			Receiver.Poll();
			NoteChanges();
		}

		internal void AfterPush(bool first)
		{
			NoteChanges();
			try
			{
				this.Pushed?.Invoke(first);
			}
			catch (Exception arg)
			{
				Journal.Error($"a Pushed handler threw: {arg}");
			}
		}

		internal void AfterSessionEnd()
		{
			NoteChanges();
		}

		private void Enrol()
		{
			GameHooks.Track(this);
			Family.Register(new FamilyEntry(Guid, Title, Version, OldestAccepted, Mandatory));
			Family.SetStatus(Guid, () => Reports.Status(this));
			Family.SetDiff(Guid, () => Reports.Diff(this));
			Family.SetVerbosity(Guid, delegate(int level)
			{
				Verbosity = (Verbosity)level;
			});
		}

		private void NoteChanges()
		{
			bool isBound = IsBound;
			bool isSteward = IsSteward;
			Tags.Update(isBound && !isSteward, Ledger);
			if (isBound != lastBound)
			{
				lastBound = isBound;
				Raise(this.BindingChanged, "BindingChanged");
			}
			if (isSteward != lastSteward)
			{
				lastSteward = isSteward;
				Raise(this.StewardChanged, "StewardChanged");
			}
		}

		private void Raise(Action? handler, string name)
		{
			try
			{
				handler?.Invoke();
			}
			catch (Exception arg)
			{
				Journal.Error($"a {name} handler threw: {arg}");
			}
		}
	}
	public sealed class Clause<T> : IClause
	{
		private readonly Ledger ledger;

		private T own;

		private T author;

		public ConfigEntry<T> Entry { get; }

		public bool Local { get; }

		public T AuthorValue
		{
			get
			{
				if (!Side.IsServer)
				{
					return author;
				}
				return Entry.Value;
			}
		}

		public T OwnValue => own;

		ConfigEntryBase IClause.Entry => (ConfigEntryBase)(object)Entry;

		string IClause.Section => ((ConfigEntryBase)Entry).Definition.Section;

		string IClause.Key => ((ConfigEntryBase)Entry).Definition.Key;

		public string AuthorToml { get; private set; }

		string IClause.OwnToml => TomlTypeConverter.ConvertToString((object)own, typeof(T));

		string IClause.CurrentToml => ((ConfigEntryBase)Entry).GetSerializedValue();

		internal Clause(ConfigEntry<T> entry, bool local, Ledger ledger)
		{
			Entry = entry;
			Local = local;
			this.ledger = ledger;
			own = entry.Value;
			author = default(T);
			AuthorToml = "";
		}

		void IClause.RememberOwn()
		{
			own = Entry.Value;
		}

		bool IClause.TakeAuthor(string toml)
		{
			try
			{
				author = TomlTypeConverter.ConvertToValue<T>(toml);
				AuthorToml = toml;
				return true;
			}
			catch (Exception)
			{
				return false;
			}
		}

		void IClause.ApplyAuthor()
		{
			ledger.Quietly((ConfigEntryBase)(object)Entry, delegate
			{
				Entry.Value = author;
			});
		}

		void IClause.RestoreOwn()
		{
			ledger.Quietly((ConfigEntryBase)(object)Entry, delegate
			{
				Entry.Value = own;
			});
		}
	}
	internal interface IClause
	{
		ConfigEntryBase Entry { get; }

		string Section { get; }

		string Key { get; }

		bool Local { get; }

		string AuthorToml { get; }

		string OwnToml { get; }

		string CurrentToml { get; }

		void RememberOwn();

		bool TakeAuthor(string toml);

		void ApplyAuthor();

		void RestoreOwn();
	}
	internal static class ConsoleCommands
	{
		[CompilerGenerated]
		private static class <>O
		{
			public static ConsoleEvent <0>__Run;

			public static Func<FamilyEntry, string> <1>__Status;

			public static Func<FamilyEntry, string> <2>__Diff;
		}

		private const string Usage = "charter status | charter diff [title] | charter versions | charter trace on|off";

		private static bool registered;

		public static void Register()
		{
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Expected O, but got Unknown
			if (!registered)
			{
				registered = true;
				object obj = <>O.<0>__Run;
				if (obj == null)
				{
					ConsoleEvent val = Run;
					<>O.<0>__Run = val;
					obj = (object)val;
				}
				new ConsoleCommand("charter", "Charter: charter status | charter diff [title] | charter versions | charter trace on|off", (ConsoleEvent)obj, false, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false);
			}
		}

		private static void Run(ConsoleEventArgs args)
		{
			string text = ((args.Length > 1) ? args[1].ToLowerInvariant() : "status");
			string rest = ((args.Length > 2) ? string.Join(" ", args.Args.Skip(2)) : "");
			foreach (string item in (text switch
			{
				"status" => Family.Entries().Select(Status), 
				"diff" => (from e in Family.Entries()
					where Matches(e, rest)
					select e).Select(Diff), 
				"versions" => Versions(), 
				"trace" => Trace(rest), 
				_ => new string[1] { "charter status | charter diff [title] | charter versions | charter trace on|off" }, 
			}).SelectMany((string l) => l.Split(new char[1] { '\n' })))
			{
				args.Context.AddString(item);
				GameHooks.LeadJournal?.Info(item);
			}
		}

		private static bool Matches(FamilyEntry entry, string title)
		{
			if (title.Length != 0)
			{
				return entry.Title.IndexOf(title, StringComparison.OrdinalIgnoreCase) >= 0;
			}
			return true;
		}

		private static string Status(FamilyEntry entry)
		{
			return Family.Status(entry.Guid)?.Invoke() ?? (entry.Title + " " + entry.Version + ": no status");
		}

		private static string Diff(FamilyEntry entry)
		{
			return Family.Diff(entry.Guid)?.Invoke() ?? (entry.Title + ": no diff");
		}

		private static IEnumerable<string> Versions()
		{
			yield return "This side:";
			foreach (FamilyEntry item in Family.Entries())
			{
				yield return "  " + item.Describe();
			}
			yield return (FamilyRpc.ServerRegistry == null) ? "Server: not received (not connected, or the server runs no Charter mod)" : "Server, as received at join:";
			foreach (FamilyEntry item2 in FamilyRpc.ServerRegistry ?? new List<FamilyEntry>())
			{
				yield return "  " + item2.Describe();
			}
		}

		private static IEnumerable<string> Trace(string mode)
		{
			if (mode != "on" && mode != "off")
			{
				return new string[1] { "charter status | charter diff [title] | charter versions | charter trace on|off" };
			}
			int num = ((!(mode == "on")) ? 1 : 2);
			foreach (FamilyEntry item in Family.Entries())
			{
				Family.Verbosity(item.Guid)?.Invoke(num);
			}
			return new string[1] { $"Charter verbosity is now {(Verbosity)num} for every mod" };
		}
	}
	internal sealed class Courier
	{
		private sealed class Lane
		{
			public int Sequence;

			public bool Steward;

			public readonly Queue<ZPackage> Fragments = new Queue<ZPackage>();
		}

		private const int HoldAboveQueued = 524288;

		private const float PruneSeconds = 5f;

		private readonly Dictionary<ZNetPeer, Lane> lanes = new Dictionary<ZNetPeer, Lane>();

		private readonly List<ZNetPeer> peers = new List<ZNetPeer>();

		private int queued;

		private float nextPrune;

		private readonly string guid;

		private readonly string rpcName;

		private readonly Journal journal;

		public Courier(string guid, Journal journal)
		{
			this.guid = guid;
			this.journal = journal;
			rpcName = "Charter_" + guid + "_Push";
		}

		public bool? LastSteward(ZNetPeer peer)
		{
			if (!lanes.TryGetValue(peer, out Lane value))
			{
				return null;
			}
			return value.Steward;
		}

		public void Send(ZNetPeer peer, PushBody body, string reason)
		{
			Send(peer, new Parcel(body), reason);
		}

		public void Send(ZNetPeer peer, Parcel parcel, string reason)
		{
			if (parcel.TooLarge)
			{
				journal.Error($"push refused: {parcel.BodyBytes} bytes exceed {8388608} ({guid})");
				return;
			}
			if (!lanes.TryGetValue(peer, out Lane value))
			{
				value = (lanes[peer] = new Lane());
			}
			value.Steward = parcel.Steward;
			int num = ++value.Sequence;
			List<ZPackage> list = Fragmenter.Split(guid, num, parcel);
			foreach (ZPackage item in list)
			{
				value.Fragments.Enqueue(item);
			}
			queued += list.Count;
			journal.Info($"push #{num} to {Side.NameOf(peer)} ({reason}): {parcel.Clauses} clause(s), {parcel.Articles} article(s), " + string.Format("{0} bytes{1}, {2} fragment(s)", parcel.WireBytes, parcel.Compressed ? " compressed" : "", list.Count));
		}

		public void Tick()
		{
			float unscaledTime = Time.unscaledTime;
			if (lanes.Count == 0 || (queued == 0 && unscaledTime < nextPrune))
			{
				return;
			}
			nextPrune = unscaledTime + 5f;
			peers.Clear();
			foreach (ZNetPeer key in lanes.Keys)
			{
				peers.Add(key);
			}
			foreach (ZNetPeer peer in peers)
			{
				SendNext(peer);
			}
		}

		private void SendNext(ZNetPeer peer)
		{
			if (!Side.IsPresent(peer))
			{
				queued -= lanes[peer].Fragments.Count;
				lanes.Remove(peer);
				return;
			}
			Lane lane = lanes[peer];
			if (lane.Fragments.Count != 0 && peer.m_socket.GetSendQueueSize() <= 524288)
			{
				peer.m_rpc.Invoke(rpcName, new object[1] { lane.Fragments.Dequeue() });
				queued--;
				if (journal.Tracing)
				{
					journal.Trace($"fragment sent to {Side.NameOf(peer)}, {lane.Fragments.Count} left in the queue");
				}
			}
		}
	}
	internal static class Family
	{
		private const string RegistryKey = "Charter.Family";

		private const string LeadKey = "Charter.Family.Lead";

		public static void Register(FamilyEntry entry)
		{
			Registry()[entry.Guid] = entry.Format();
		}

		public static bool ClaimLead(string guid)
		{
			if (AppDomain.CurrentDomain.GetData("Charter.Family.Lead") is string text)
			{
				return text == guid;
			}
			AppDomain.CurrentDomain.SetData("Charter.Family.Lead", guid);
			return true;
		}

		public static List<FamilyEntry> Entries()
		{
			return (from pair in Registry()
				select FamilyEntry.Parse(pair.Key, pair.Value)).OrderBy<FamilyEntry, string>((FamilyEntry e) => e.Title, StringComparer.OrdinalIgnoreCase).ToList();
		}

		public static void SetStatus(string guid, Func<string> report)
		{
			AppDomain.CurrentDomain.SetData("Charter.Status." + guid, report);
		}

		public static void SetDiff(string guid, Func<string> report)
		{
			AppDomain.CurrentDomain.SetData("Charter.Diff." + guid, report);
		}

		public static void SetVerbosity(string guid, Action<int> set)
		{
			AppDomain.CurrentDomain.SetData("Charter.Verbosity." + guid, set);
		}

		public static Func<string>? Status(string guid)
		{
			return AppDomain.CurrentDomain.GetData("Charter.Status." + guid) as Func<string>;
		}

		public static Func<string>? Diff(string guid)
		{
			return AppDomain.CurrentDomain.GetData("Charter.Diff." + guid) as Func<string>;
		}

		public static Action<int>? Verbosity(string guid)
		{
			return AppDomain.CurrentDomain.GetData("Charter.Verbosity." + guid) as Action<int>;
		}

		private static Dictionary<string, string> Registry()
		{
			if (AppDomain.CurrentDomain.GetData("Charter.Family") is Dictionary<string, string> result)
			{
				return result;
			}
			Dictionary<string, string> dictionary = new Dictionary<string, string>(StringComparer.Ordinal);
			AppDomain.CurrentDomain.SetData("Charter.Family", dictionary);
			return dictionary;
		}
	}
	internal static class FamilyCheck
	{
		private const string Alphabet = "abcdefghijklmnopqrstuvwxyz0123456789";

		private static readonly Random Dice = new Random();

		public static List<string> Compare(List<FamilyEntry> server, List<FamilyEntry> client)
		{
			List<string> list = new List<string>();
			foreach (FamilyEntry mine in server)
			{
				FamilyEntry familyEntry = client.FirstOrDefault((FamilyEntry c) => c.Guid == mine.Guid);
				if (familyEntry == null)
				{
					if (mine.Mandatory)
					{
						list.Add(Outdated(mine, "no copy"));
					}
				}
				else if (Older(familyEntry.Version, mine.OldestAccepted) || Older(mine.Version, familyEntry.OldestAccepted))
				{
					list.Add(Outdated(mine, familyEntry.Version));
				}
			}
			list.AddRange(client.Where((FamilyEntry c) => c.Mandatory && server.All((FamilyEntry s) => s.Guid !=